spessasynth_lib 3.25.2 → 3.25.4
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/midi_parser/midi_editor.js +5 -5
- package/midi_parser/rmidi_writer.js +11 -6
- package/midi_parser/used_keys_loaded.js +3 -3
- package/package.json +1 -1
- package/sequencer/sequencer.js +34 -12
- package/sequencer/worklet_sequencer/events.js +7 -1
- package/sequencer/worklet_sequencer/sequencer_message.js +10 -8
- package/sequencer/worklet_sequencer/song_control.js +3 -1
- package/synthetizer/worklet_processor.min.js +10 -10
- package/synthetizer/worklet_system/worklet_methods/controller_control/reset_controllers.js +2 -1
- package/synthetizer/worklet_system/worklet_methods/soundfont_management/get_preset.js +4 -2
- package/synthetizer/worklet_system/worklet_methods/system_exclusive.js +5 -4
- package/synthetizer/worklet_system/worklet_utilities/lowpass_filter.js +2 -3
- package/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +2 -2
- package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +2 -1
- package/utils/sysex_detector.js +13 -1
- package/utils/xg_hacks.js +32 -15
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from "../../worklet_utilities/controller_tables.js";
|
|
12
12
|
import { midiControllers } from "../../../../midi_parser/midi_message.js";
|
|
13
13
|
import { DEFAULT_PERCUSSION, DEFAULT_SYNTH_MODE } from "../../../synth_constants.js";
|
|
14
|
+
import { getDefaultBank } from "../../../../utils/xg_hacks.js";
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -38,7 +39,7 @@ export function resetAllControllers(log = true)
|
|
|
38
39
|
// if preset is unlocked, switch to non-drums and call event
|
|
39
40
|
if (!ch.lockPreset)
|
|
40
41
|
{
|
|
41
|
-
ch.setBankSelect(
|
|
42
|
+
ch.setBankSelect(getDefaultBank(this.system));
|
|
42
43
|
if (channelNumber % 16 === DEFAULT_PERCUSSION)
|
|
43
44
|
{
|
|
44
45
|
ch.setPreset(this.drumPreset);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isSystemXG } from "../../../../utils/xg_hacks.js";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @this {SpessaSynthProcessor}
|
|
3
5
|
* @param program {number}
|
|
@@ -10,11 +12,11 @@ export function getPreset(bank, program)
|
|
|
10
12
|
{
|
|
11
13
|
// if override soundfont
|
|
12
14
|
const bankWithOffset = bank === 128 ? 128 : bank - this.soundfontBankOffset;
|
|
13
|
-
const preset = this.overrideSoundfont.getPresetNoFallback(bankWithOffset, program, this.system
|
|
15
|
+
const preset = this.overrideSoundfont.getPresetNoFallback(bankWithOffset, program, isSystemXG(this.system));
|
|
14
16
|
if (preset)
|
|
15
17
|
{
|
|
16
18
|
return preset;
|
|
17
19
|
}
|
|
18
20
|
}
|
|
19
|
-
return this.soundfontManager.getPreset(bank, program, this.system
|
|
21
|
+
return this.soundfontManager.getPreset(bank, program, isSystemXG(this.system));
|
|
20
22
|
}
|
|
@@ -2,6 +2,7 @@ import { arrayToHexString, consoleColors } from "../../../utils/other.js";
|
|
|
2
2
|
import { SpessaSynthInfo, SpessaSynthWarn } from "../../../utils/loggin.js";
|
|
3
3
|
import { midiControllers } from "../../../midi_parser/midi_message.js";
|
|
4
4
|
import { ALL_CHANNELS_OR_DIFFERENT_ACTION } from "../message_protocol/worklet_message.js";
|
|
5
|
+
import { isSystemXG } from "../../../utils/xg_hacks.js";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* KeyNum: tuning
|
|
@@ -147,7 +148,7 @@ export function systemExclusive(messageData, channelOffset = 0)
|
|
|
147
148
|
// gm system related
|
|
148
149
|
if (messageData[3] === 0x01)
|
|
149
150
|
{
|
|
150
|
-
SpessaSynthInfo("%
|
|
151
|
+
SpessaSynthInfo("%cGM1 system on", consoleColors.info);
|
|
151
152
|
this.setSystem("gm");
|
|
152
153
|
}
|
|
153
154
|
else if (messageData[3] === 0x03)
|
|
@@ -614,7 +615,7 @@ export function systemExclusive(messageData, channelOffset = 0)
|
|
|
614
615
|
// XG part parameter
|
|
615
616
|
if (messageData[3] === 0x08)
|
|
616
617
|
{
|
|
617
|
-
if (this.system
|
|
618
|
+
if (!isSystemXG(this.system))
|
|
618
619
|
{
|
|
619
620
|
return;
|
|
620
621
|
}
|
|
@@ -714,7 +715,7 @@ export function systemExclusive(messageData, channelOffset = 0)
|
|
|
714
715
|
}
|
|
715
716
|
);
|
|
716
717
|
}
|
|
717
|
-
else if (this.system
|
|
718
|
+
else if (isSystemXG(this.system))
|
|
718
719
|
{
|
|
719
720
|
SpessaSynthWarn(
|
|
720
721
|
`%cUnrecognized Yamaha XG SysEx: %c${arrayToHexString(messageData)}`,
|
|
@@ -726,7 +727,7 @@ export function systemExclusive(messageData, channelOffset = 0)
|
|
|
726
727
|
}
|
|
727
728
|
else
|
|
728
729
|
{
|
|
729
|
-
if (this.system
|
|
730
|
+
if (isSystemXG(this.system))
|
|
730
731
|
{
|
|
731
732
|
SpessaSynthWarn(
|
|
732
733
|
`%cUnrecognized Yamaha SysEx: %c${arrayToHexString(messageData)}`,
|
|
@@ -139,20 +139,19 @@ export class WorkletLowpassFilter
|
|
|
139
139
|
|
|
140
140
|
// the final cutoff for this calculation
|
|
141
141
|
const targetCutoff = filter.currentInitialFc + fcExcursion;
|
|
142
|
-
|
|
142
|
+
const modulatedResonance = voice.modulatedGenerators[generatorTypes.initialFilterQ];
|
|
143
143
|
/* note:
|
|
144
144
|
* the check for initialFC is because of the filter optimization
|
|
145
145
|
* (if cents are the maximum then the filter is open)
|
|
146
146
|
* filter cannot use this optimization if it's dynamic (see #53), and
|
|
147
147
|
* the filter can only be dynamic if the initial filter is not open
|
|
148
148
|
*/
|
|
149
|
-
if (filter.currentInitialFc > 13499 && targetCutoff > 13499 &&
|
|
149
|
+
if (filter.currentInitialFc > 13499 && targetCutoff > 13499 && modulatedResonance === 0)
|
|
150
150
|
{
|
|
151
151
|
filter.currentInitialFc = 13500;
|
|
152
152
|
return; // filter is open
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
const modulatedResonance = voice.modulatedGenerators[generatorTypes.initialFilterQ];
|
|
156
155
|
// check if the frequency has changed. if so, calculate new coefficients
|
|
157
156
|
if (Math.abs(filter.lastTargetCutoff - targetCutoff) > 1 || filter.resonanceCb !== modulatedResonance)
|
|
158
157
|
{
|
|
@@ -27,7 +27,7 @@ import { channelPressure } from "../worklet_methods/tuning_control/channel_press
|
|
|
27
27
|
import { pitchWheel } from "../worklet_methods/tuning_control/pitch_wheel.js";
|
|
28
28
|
import { setOctaveTuning } from "../worklet_methods/tuning_control/set_octave_tuning.js";
|
|
29
29
|
import { programChange } from "../worklet_methods/program_change.js";
|
|
30
|
-
import { chooseBank, parseBankSelect } from "../../../utils/xg_hacks.js";
|
|
30
|
+
import { chooseBank, isSystemXG, parseBankSelect } from "../../../utils/xg_hacks.js";
|
|
31
31
|
import { DEFAULT_PERCUSSION } from "../../synth_constants.js";
|
|
32
32
|
|
|
33
33
|
/**
|
|
@@ -205,7 +205,7 @@ class WorkletProcessorChannel
|
|
|
205
205
|
|
|
206
206
|
get isXGChannel()
|
|
207
207
|
{
|
|
208
|
-
return this.synth.system
|
|
208
|
+
return isSystemXG(this.synth.system) || (this.lockPreset && isSystemXG(this.lockedSystem));
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
/**
|
|
@@ -10,6 +10,7 @@ import { WorkletVolumeEnvelope } from "./volume_envelope.js";
|
|
|
10
10
|
import { WorkletModulationEnvelope } from "./modulation_envelope.js";
|
|
11
11
|
import { addAndClampGenerator, generatorTypes } from "../../../soundfont/basic_soundfont/generator.js";
|
|
12
12
|
import { Modulator } from "../../../soundfont/basic_soundfont/modulator.js";
|
|
13
|
+
import { isSystemXG } from "../../../utils/xg_hacks.js";
|
|
13
14
|
|
|
14
15
|
const EXCLUSIVE_CUTOFF_TIME = -2320;
|
|
15
16
|
const EXCLUSIVE_MOD_CUTOFF_TIME = -1130; // less because filter shenanigans
|
|
@@ -408,7 +409,7 @@ export function getWorkletVoices(channel,
|
|
|
408
409
|
let preset = channelObject.preset;
|
|
409
410
|
if (overridePatch)
|
|
410
411
|
{
|
|
411
|
-
preset = this.soundfontManager.getPreset(bank, program, this.system
|
|
412
|
+
preset = this.soundfontManager.getPreset(bank, program, isSystemXG(this.system));
|
|
412
413
|
}
|
|
413
414
|
/**
|
|
414
415
|
* @returns {WorkletVoice[]}
|
package/utils/sysex_detector.js
CHANGED
|
@@ -42,5 +42,17 @@ export function isGSOn(e)
|
|
|
42
42
|
export function isGMOn(e)
|
|
43
43
|
{
|
|
44
44
|
return e.messageData[0] === 0x7E // non realtime
|
|
45
|
-
&& e.messageData[2] === 0x09
|
|
45
|
+
&& e.messageData[2] === 0x09 // gm system
|
|
46
|
+
&& e.messageData[3] === 0x01; // gm1
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param e {MIDIMessage}
|
|
51
|
+
* @returns boolean
|
|
52
|
+
*/
|
|
53
|
+
export function isGM2On(e)
|
|
54
|
+
{
|
|
55
|
+
return e.messageData[0] === 0x7E // non realtime
|
|
56
|
+
&& e.messageData[2] === 0x09 // gm system
|
|
57
|
+
&& e.messageData[3] === 0x03; // gm2
|
|
46
58
|
}
|
package/utils/xg_hacks.js
CHANGED
|
@@ -4,6 +4,17 @@ import { DEFAULT_PERCUSSION } from "../synthetizer/synth_constants.js";
|
|
|
4
4
|
|
|
5
5
|
export const XG_SFX_VOICE = 64;
|
|
6
6
|
|
|
7
|
+
const GM2_DEFAULT_BANK = 121;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param sys {SynthSystem}
|
|
11
|
+
* @returns {number}
|
|
12
|
+
*/
|
|
13
|
+
export function getDefaultBank(sys)
|
|
14
|
+
{
|
|
15
|
+
return sys === "gm2" ? GM2_DEFAULT_BANK : 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
7
18
|
/**
|
|
8
19
|
* @param bankNr {number}
|
|
9
20
|
* @returns {boolean}
|
|
@@ -19,7 +30,7 @@ export function isXGDrums(bankNr)
|
|
|
19
30
|
*/
|
|
20
31
|
export function isValidXGMSB(bank)
|
|
21
32
|
{
|
|
22
|
-
return isXGDrums(bank) || bank === XG_SFX_VOICE;
|
|
33
|
+
return isXGDrums(bank) || bank === XG_SFX_VOICE || bank === GM2_DEFAULT_BANK;
|
|
23
34
|
}
|
|
24
35
|
|
|
25
36
|
/**
|
|
@@ -42,7 +53,7 @@ export function parseBankSelect(bankBefore, bank, system, isLSB, isDrums, channe
|
|
|
42
53
|
let drumsStatus = 0;
|
|
43
54
|
if (isLSB)
|
|
44
55
|
{
|
|
45
|
-
if (system
|
|
56
|
+
if (isSystemXG(system))
|
|
46
57
|
{
|
|
47
58
|
if (!isValidXGMSB(bank))
|
|
48
59
|
{
|
|
@@ -150,27 +161,33 @@ export function chooseBank(msb, lsb, isDrums, isXG)
|
|
|
150
161
|
else
|
|
151
162
|
{
|
|
152
163
|
// check for SFX
|
|
153
|
-
if (msb
|
|
164
|
+
if (isValidXGMSB(msb))
|
|
154
165
|
{
|
|
155
|
-
|
|
156
|
-
if (lsb === 0 && msb !== 0)
|
|
157
|
-
{
|
|
158
|
-
return msb;
|
|
159
|
-
}
|
|
160
|
-
if (!isValidXGMSB(lsb))
|
|
161
|
-
{
|
|
162
|
-
return lsb;
|
|
163
|
-
}
|
|
164
|
-
return 0;
|
|
166
|
+
return msb;
|
|
165
167
|
}
|
|
166
|
-
|
|
168
|
+
// if lsb is 0 and msb is not, use that
|
|
169
|
+
if (lsb === 0 && msb !== 0)
|
|
170
|
+
{
|
|
171
|
+
return msb;
|
|
172
|
+
}
|
|
173
|
+
if (!isValidXGMSB(lsb))
|
|
167
174
|
{
|
|
168
|
-
return
|
|
175
|
+
return lsb;
|
|
169
176
|
}
|
|
177
|
+
return 0;
|
|
170
178
|
}
|
|
171
179
|
}
|
|
172
180
|
else
|
|
173
181
|
{
|
|
174
182
|
return isDrums ? 128 : msb;
|
|
175
183
|
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* @param system {SynthSystem}
|
|
188
|
+
* @returns boolean
|
|
189
|
+
*/
|
|
190
|
+
export function isSystemXG(system)
|
|
191
|
+
{
|
|
192
|
+
return system === "gm2" || system === "xg";
|
|
176
193
|
}
|