spessasynth_core 1.1.2 → 1.1.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/LICENSE +3 -26
- package/README.md +156 -474
- package/index.js +73 -8
- package/package.json +21 -8
- package/src/externals/fflate/LICENSE +21 -0
- package/src/externals/fflate/fflate.min.js +1 -0
- package/src/externals/stbvorbis_sync/@types/stbvorbis_sync.d.ts +12 -0
- package/src/externals/stbvorbis_sync/LICENSE +202 -0
- package/src/externals/stbvorbis_sync/NOTICE +6 -0
- package/src/externals/stbvorbis_sync/stbvorbis_sync.min.js +1 -0
- package/src/midi/README.md +32 -0
- package/src/midi/basic_midi.js +567 -0
- package/src/midi/midi_builder.js +202 -0
- package/src/midi/midi_loader.js +324 -0
- package/{spessasynth_core/midi_parser → src/midi}/midi_message.js +58 -35
- package/src/midi/midi_sequence.js +224 -0
- package/src/midi/midi_tools/get_note_times.js +154 -0
- package/src/midi/midi_tools/midi_editor.js +611 -0
- package/src/midi/midi_tools/midi_writer.js +99 -0
- package/src/midi/midi_tools/rmidi_writer.js +567 -0
- package/src/midi/midi_tools/used_keys_loaded.js +238 -0
- package/src/midi/xmf_loader.js +454 -0
- package/src/sequencer/README.md +5 -0
- package/src/sequencer/events.js +81 -0
- package/src/sequencer/play.js +349 -0
- package/src/sequencer/process_event.js +165 -0
- package/{spessasynth_core/sequencer/worklet_sequencer → src/sequencer}/process_tick.js +103 -84
- package/src/sequencer/sequencer_engine.js +367 -0
- package/src/sequencer/song_control.js +201 -0
- package/src/soundfont/README.md +13 -0
- package/src/soundfont/basic_soundfont/basic_instrument.js +77 -0
- package/src/soundfont/basic_soundfont/basic_preset.js +336 -0
- package/src/soundfont/basic_soundfont/basic_sample.js +206 -0
- package/src/soundfont/basic_soundfont/basic_soundfont.js +565 -0
- package/src/soundfont/basic_soundfont/basic_zone.js +64 -0
- package/src/soundfont/basic_soundfont/basic_zones.js +43 -0
- package/src/soundfont/basic_soundfont/generator.js +220 -0
- package/src/soundfont/basic_soundfont/modulator.js +378 -0
- package/src/soundfont/basic_soundfont/riff_chunk.js +149 -0
- package/src/soundfont/basic_soundfont/write_dls/art2.js +173 -0
- package/src/soundfont/basic_soundfont/write_dls/articulator.js +49 -0
- package/src/soundfont/basic_soundfont/write_dls/combine_zones.js +400 -0
- package/src/soundfont/basic_soundfont/write_dls/ins.js +103 -0
- package/src/soundfont/basic_soundfont/write_dls/lins.js +18 -0
- package/src/soundfont/basic_soundfont/write_dls/modulator_converter.js +330 -0
- package/src/soundfont/basic_soundfont/write_dls/rgn2.js +121 -0
- package/src/soundfont/basic_soundfont/write_dls/wave.js +94 -0
- package/src/soundfont/basic_soundfont/write_dls/write_dls.js +119 -0
- package/src/soundfont/basic_soundfont/write_dls/wsmp.js +78 -0
- package/src/soundfont/basic_soundfont/write_dls/wvpl.js +32 -0
- package/src/soundfont/basic_soundfont/write_sf2/ibag.js +39 -0
- package/src/soundfont/basic_soundfont/write_sf2/igen.js +80 -0
- package/src/soundfont/basic_soundfont/write_sf2/imod.js +46 -0
- package/src/soundfont/basic_soundfont/write_sf2/inst.js +34 -0
- package/src/soundfont/basic_soundfont/write_sf2/pbag.js +39 -0
- package/src/soundfont/basic_soundfont/write_sf2/pgen.js +82 -0
- package/src/soundfont/basic_soundfont/write_sf2/phdr.js +42 -0
- package/src/soundfont/basic_soundfont/write_sf2/pmod.js +46 -0
- package/src/soundfont/basic_soundfont/write_sf2/sdta.js +80 -0
- package/src/soundfont/basic_soundfont/write_sf2/shdr.js +55 -0
- package/src/soundfont/basic_soundfont/write_sf2/write.js +222 -0
- package/src/soundfont/dls/articulator_converter.js +396 -0
- package/src/soundfont/dls/dls_destinations.js +38 -0
- package/src/soundfont/dls/dls_preset.js +44 -0
- package/src/soundfont/dls/dls_sample.js +75 -0
- package/src/soundfont/dls/dls_soundfont.js +186 -0
- package/src/soundfont/dls/dls_sources.js +62 -0
- package/src/soundfont/dls/dls_zone.js +95 -0
- package/src/soundfont/dls/read_articulation.js +299 -0
- package/src/soundfont/dls/read_instrument.js +121 -0
- package/src/soundfont/dls/read_instrument_list.js +17 -0
- package/src/soundfont/dls/read_lart.js +35 -0
- package/src/soundfont/dls/read_region.js +152 -0
- package/src/soundfont/dls/read_samples.js +270 -0
- package/src/soundfont/load_soundfont.js +21 -0
- package/src/soundfont/read_sf2/generators.js +46 -0
- package/{spessasynth_core/soundfont/chunk → src/soundfont/read_sf2}/instruments.js +20 -14
- package/src/soundfont/read_sf2/modulators.js +36 -0
- package/src/soundfont/read_sf2/presets.js +80 -0
- package/src/soundfont/read_sf2/samples.js +304 -0
- package/src/soundfont/read_sf2/soundfont.js +305 -0
- package/{spessasynth_core/soundfont/chunk → src/soundfont/read_sf2}/zones.js +68 -69
- package/src/synthetizer/README.md +7 -0
- package/src/synthetizer/audio_engine/README.md +9 -0
- package/src/synthetizer/audio_engine/engine_components/compute_modulator.js +266 -0
- package/src/synthetizer/audio_engine/engine_components/controller_tables.js +88 -0
- package/src/synthetizer/audio_engine/engine_components/key_modifier_manager.js +150 -0
- package/{spessasynth_core/synthetizer/worklet_system/worklet_utilities → src/synthetizer/audio_engine/engine_components}/lfo.js +9 -6
- package/src/synthetizer/audio_engine/engine_components/lowpass_filter.js +282 -0
- package/src/synthetizer/audio_engine/engine_components/midi_audio_channel.js +467 -0
- package/src/synthetizer/audio_engine/engine_components/modulation_envelope.js +181 -0
- package/{spessasynth_core/synthetizer/worklet_system/worklet_utilities → src/synthetizer/audio_engine/engine_components}/modulator_curves.js +33 -30
- package/src/synthetizer/audio_engine/engine_components/soundfont_manager.js +221 -0
- package/src/synthetizer/audio_engine/engine_components/stereo_panner.js +120 -0
- package/{spessasynth_core/synthetizer/worklet_system/worklet_utilities → src/synthetizer/audio_engine/engine_components}/unit_converter.js +11 -4
- package/src/synthetizer/audio_engine/engine_components/voice.js +519 -0
- package/src/synthetizer/audio_engine/engine_components/volume_envelope.js +401 -0
- package/src/synthetizer/audio_engine/engine_components/wavetable_oscillator.js +263 -0
- package/src/synthetizer/audio_engine/engine_methods/controller_control/controller_change.js +132 -0
- package/src/synthetizer/audio_engine/engine_methods/controller_control/master_parameters.js +48 -0
- package/src/synthetizer/audio_engine/engine_methods/controller_control/reset_controllers.js +241 -0
- package/src/synthetizer/audio_engine/engine_methods/create_midi_channel.js +27 -0
- package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_coarse.js +253 -0
- package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_fine.js +66 -0
- package/src/synthetizer/audio_engine/engine_methods/mute_channel.js +17 -0
- package/src/synthetizer/audio_engine/engine_methods/note_on.js +175 -0
- package/src/synthetizer/audio_engine/engine_methods/portamento_time.js +92 -0
- package/src/synthetizer/audio_engine/engine_methods/program_change.js +61 -0
- package/src/synthetizer/audio_engine/engine_methods/render_voice.js +196 -0
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/clear_sound_font.js +30 -0
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/get_preset.js +22 -0
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/reload_sound_font.js +28 -0
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/send_preset_list.js +31 -0
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/set_embedded_sound_font.js +21 -0
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/kill_note.js +20 -0
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/note_off.js +55 -0
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_channels.js +16 -0
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_notes.js +30 -0
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/voice_killing.js +63 -0
- package/src/synthetizer/audio_engine/engine_methods/system_exclusive.js +776 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/channel_pressure.js +24 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/pitch_wheel.js +33 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/poly_pressure.js +31 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_master_tuning.js +15 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_modulation_depth.js +27 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_octave_tuning.js +19 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_tuning.js +27 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_all_channels.js +15 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_channel.js +34 -0
- package/src/synthetizer/audio_engine/main_processor.js +804 -0
- package/src/synthetizer/audio_engine/snapshot/apply_synthesizer_snapshot.js +15 -0
- package/src/synthetizer/audio_engine/snapshot/channel_snapshot.js +175 -0
- package/src/synthetizer/audio_engine/snapshot/synthesizer_snapshot.js +116 -0
- package/src/synthetizer/synth_constants.js +22 -0
- package/{spessasynth_core → src}/utils/README.md +1 -0
- package/src/utils/buffer_to_wav.js +185 -0
- package/src/utils/byte_functions/big_endian.js +32 -0
- package/src/utils/byte_functions/little_endian.js +77 -0
- package/src/utils/byte_functions/string.js +107 -0
- package/src/utils/byte_functions/variable_length_quantity.js +42 -0
- package/src/utils/fill_with_defaults.js +21 -0
- package/src/utils/indexed_array.js +52 -0
- package/{spessasynth_core → src}/utils/loggin.js +70 -78
- package/src/utils/other.js +92 -0
- package/src/utils/sysex_detector.js +58 -0
- package/src/utils/xg_hacks.js +193 -0
- package/.idea/inspectionProfiles/Project_Default.xml +0 -10
- package/.idea/jsLibraryMappings.xml +0 -6
- package/.idea/modules.xml +0 -8
- package/.idea/spessasynth_core.iml +0 -12
- package/.idea/vcs.xml +0 -6
- package/spessasynth_core/midi_parser/README.md +0 -3
- package/spessasynth_core/midi_parser/midi_loader.js +0 -386
- package/spessasynth_core/sequencer/sequencer.js +0 -202
- package/spessasynth_core/sequencer/worklet_sequencer/play.js +0 -209
- package/spessasynth_core/sequencer/worklet_sequencer/process_event.js +0 -120
- package/spessasynth_core/sequencer/worklet_sequencer/song_control.js +0 -112
- package/spessasynth_core/soundfont/README.md +0 -4
- package/spessasynth_core/soundfont/chunk/generators.js +0 -205
- package/spessasynth_core/soundfont/chunk/modulators.js +0 -232
- package/spessasynth_core/soundfont/chunk/presets.js +0 -264
- package/spessasynth_core/soundfont/chunk/riff_chunk.js +0 -46
- package/spessasynth_core/soundfont/chunk/samples.js +0 -250
- package/spessasynth_core/soundfont/soundfont_parser.js +0 -301
- package/spessasynth_core/synthetizer/README.md +0 -6
- package/spessasynth_core/synthetizer/synthesizer.js +0 -303
- package/spessasynth_core/synthetizer/worklet_system/README.md +0 -3
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/controller_control.js +0 -290
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/data_entry.js +0 -280
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/note_off.js +0 -102
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/note_on.js +0 -77
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/program_control.js +0 -140
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/system_exclusive.js +0 -266
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/tuning_control.js +0 -104
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/vibrato_control.js +0 -29
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/voice_control.js +0 -222
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/lowpass_filter.js +0 -95
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/modulation_envelope.js +0 -73
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/stereo_panner.js +0 -76
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/volume_envelope.js +0 -194
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/wavetable_oscillator.js +0 -83
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/worklet_modulator.js +0 -173
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +0 -106
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +0 -313
- package/spessasynth_core/utils/buffer_to_wav.js +0 -70
- package/spessasynth_core/utils/byte_functions.js +0 -141
- package/spessasynth_core/utils/other.js +0 -49
- package/spessasynth_core/utils/shiftable_array.js +0 -26
- package/spessasynth_core/utils/stbvorbis_sync.js +0 -1877
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { consoleColors } from "../../../../utils/other.js";
|
|
2
|
+
import { SpessaSynthInfo } from "../../../../utils/loggin.js";
|
|
3
|
+
import { modulatorSources } from "../../../../soundfont/basic_soundfont/modulator.js";
|
|
4
|
+
import { customControllers, dataEntryStates, NON_CC_INDEX_OFFSET } from "../../engine_components/controller_tables.js";
|
|
5
|
+
import { midiControllers } from "../../../../midi/midi_message.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Executes a data entry for an RPN tuning
|
|
9
|
+
* @param dataValue {number} dataEntry LSB
|
|
10
|
+
* @this {MidiAudioChannel}
|
|
11
|
+
* @private
|
|
12
|
+
*/
|
|
13
|
+
export function dataEntryFine(dataValue)
|
|
14
|
+
{
|
|
15
|
+
switch (this.dataEntryState)
|
|
16
|
+
{
|
|
17
|
+
default:
|
|
18
|
+
break;
|
|
19
|
+
|
|
20
|
+
case dataEntryStates.RPCoarse:
|
|
21
|
+
case dataEntryStates.RPFine:
|
|
22
|
+
const rpnValue = this.midiControllers[midiControllers.RPNMsb] | (this.midiControllers[midiControllers.RPNLsb] >> 7);
|
|
23
|
+
switch (rpnValue)
|
|
24
|
+
{
|
|
25
|
+
default:
|
|
26
|
+
break;
|
|
27
|
+
|
|
28
|
+
// pitch bend range fine tune
|
|
29
|
+
case 0x0000:
|
|
30
|
+
if (dataValue === 0)
|
|
31
|
+
{
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
// 14-bit value, so upper 7 are coarse and lower 7 are fine!
|
|
35
|
+
this.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheelRange] |= dataValue;
|
|
36
|
+
const actualTune = (this.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheelRange] >> 7) + dataValue / 128;
|
|
37
|
+
SpessaSynthInfo(
|
|
38
|
+
`%cChannel ${this.channelNumber} bend range. Semitones: %c${actualTune}`,
|
|
39
|
+
consoleColors.info,
|
|
40
|
+
consoleColors.value
|
|
41
|
+
);
|
|
42
|
+
break;
|
|
43
|
+
|
|
44
|
+
// fine-tuning
|
|
45
|
+
case 0x0001:
|
|
46
|
+
// grab the data and shift
|
|
47
|
+
const coarse = this.customControllers[customControllers.channelTuning];
|
|
48
|
+
const finalTuning = (coarse << 7) | dataValue;
|
|
49
|
+
this.setTuning(finalTuning * 0.01220703125); // multiply by 8192 / 100 (cent increments)
|
|
50
|
+
break;
|
|
51
|
+
|
|
52
|
+
// modulation depth
|
|
53
|
+
case 0x0005:
|
|
54
|
+
const currentModulationDepthCents = this.customControllers[customControllers.modulationMultiplier] * 50;
|
|
55
|
+
let cents = currentModulationDepthCents + (dataValue / 128) * 100;
|
|
56
|
+
this.setModulationDepth(cents);
|
|
57
|
+
break;
|
|
58
|
+
|
|
59
|
+
case 0x3FFF:
|
|
60
|
+
this.resetParameters();
|
|
61
|
+
break;
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param isMuted {boolean}
|
|
3
|
+
* @this {MidiAudioChannel}
|
|
4
|
+
*/
|
|
5
|
+
export function muteChannel(isMuted)
|
|
6
|
+
{
|
|
7
|
+
if (isMuted)
|
|
8
|
+
{
|
|
9
|
+
this.stopAllNotes(true);
|
|
10
|
+
}
|
|
11
|
+
this.isMuted = isMuted;
|
|
12
|
+
this.sendChannelProperty();
|
|
13
|
+
this.synth.callEvent("mutechannel", {
|
|
14
|
+
channel: this.channelNumber,
|
|
15
|
+
isMuted: isMuted
|
|
16
|
+
});
|
|
17
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { computeModulators } from "../engine_components/compute_modulator.js";
|
|
2
|
+
import { generatorTypes } from "../../../soundfont/basic_soundfont/generator.js";
|
|
3
|
+
import { midiControllers } from "../../../midi/midi_message.js";
|
|
4
|
+
import { portamentoTimeToSeconds } from "./portamento_time.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* sends a "MIDI Note on message"
|
|
8
|
+
* @param midiNote {number}
|
|
9
|
+
* @param velocity {number}
|
|
10
|
+
* @this {MidiAudioChannel}
|
|
11
|
+
*/
|
|
12
|
+
export function noteOn(midiNote, velocity)
|
|
13
|
+
{
|
|
14
|
+
if (velocity < 1)
|
|
15
|
+
{
|
|
16
|
+
this.noteOff(midiNote);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
velocity = Math.min(127, velocity);
|
|
20
|
+
|
|
21
|
+
if (
|
|
22
|
+
(this.synth.highPerformanceMode && this.synth.totalVoicesAmount > 200 && velocity < 40) ||
|
|
23
|
+
(this.synth.highPerformanceMode && velocity < 10) ||
|
|
24
|
+
(this.isMuted)
|
|
25
|
+
)
|
|
26
|
+
{
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const realKey = midiNote + this.channelTransposeKeyShift;
|
|
31
|
+
let sentMidiNote = realKey;
|
|
32
|
+
|
|
33
|
+
if (realKey > 127 || realKey < 0)
|
|
34
|
+
{
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const program = this.preset.program;
|
|
38
|
+
const tune = this.synth.tunings[program]?.[realKey]?.midiNote;
|
|
39
|
+
if (tune >= 0)
|
|
40
|
+
{
|
|
41
|
+
sentMidiNote = tune;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// velocity override
|
|
45
|
+
if (this.velocityOverride > 0)
|
|
46
|
+
{
|
|
47
|
+
velocity = this.velocityOverride;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// key velocity override
|
|
51
|
+
const keyVel = this.synth.keyModifierManager.getVelocity(this.channelNumber, realKey);
|
|
52
|
+
if (keyVel > -1)
|
|
53
|
+
{
|
|
54
|
+
velocity = keyVel;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// gain
|
|
58
|
+
const voiceGain = this.synth.keyModifierManager.getGain(this.channelNumber, realKey);
|
|
59
|
+
|
|
60
|
+
// portamento
|
|
61
|
+
let portamentoFromKey = -1;
|
|
62
|
+
let portamentoDuration = 0;
|
|
63
|
+
// note: the 14-bit value needs to go down to 7-bit
|
|
64
|
+
const portamentoTime = this.midiControllers[midiControllers.portamentoTime] >> 7;
|
|
65
|
+
const control = this.midiControllers[midiControllers.portamentoControl];
|
|
66
|
+
const currentFromKey = control >> 7;
|
|
67
|
+
if (
|
|
68
|
+
!this.drumChannel && // no portamento on drum channel
|
|
69
|
+
currentFromKey !== sentMidiNote && // if the same note, there's no portamento
|
|
70
|
+
this.midiControllers[midiControllers.portamentoOnOff] >= 8192 && // (64 << 7)
|
|
71
|
+
portamentoTime > 0 // 0 duration is no portamento
|
|
72
|
+
)
|
|
73
|
+
{
|
|
74
|
+
// a value of one means the initial portamento
|
|
75
|
+
if (control !== 1)
|
|
76
|
+
{
|
|
77
|
+
const diff = Math.abs(sentMidiNote - currentFromKey);
|
|
78
|
+
portamentoDuration = portamentoTimeToSeconds(portamentoTime, diff);
|
|
79
|
+
portamentoFromKey = currentFromKey;
|
|
80
|
+
}
|
|
81
|
+
// set portamento control to previous value
|
|
82
|
+
this.controllerChange(midiControllers.portamentoControl, sentMidiNote);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// get voices
|
|
86
|
+
const voices = this.synth.getVoices(
|
|
87
|
+
this.channelNumber,
|
|
88
|
+
sentMidiNote,
|
|
89
|
+
velocity,
|
|
90
|
+
realKey
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// zero means disabled
|
|
94
|
+
let panOverride = 0;
|
|
95
|
+
if (this.randomPan)
|
|
96
|
+
{
|
|
97
|
+
// the range is -500 to 500
|
|
98
|
+
panOverride = Math.round(Math.random() * 1000 - 500);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// add voices
|
|
102
|
+
const channelVoices = this.voices;
|
|
103
|
+
voices.forEach(voice =>
|
|
104
|
+
{
|
|
105
|
+
// apply portamento
|
|
106
|
+
voice.portamentoFromKey = portamentoFromKey;
|
|
107
|
+
voice.portamentoDuration = portamentoDuration;
|
|
108
|
+
|
|
109
|
+
// apply pan override
|
|
110
|
+
voice.overridePan = panOverride;
|
|
111
|
+
|
|
112
|
+
// apply gain override
|
|
113
|
+
voice.gain = voiceGain;
|
|
114
|
+
|
|
115
|
+
// apply exclusive class
|
|
116
|
+
const exclusive = voice.exclusiveClass;
|
|
117
|
+
if (exclusive !== 0)
|
|
118
|
+
{
|
|
119
|
+
// kill all voices with the same exclusive class
|
|
120
|
+
channelVoices.forEach(v =>
|
|
121
|
+
{
|
|
122
|
+
if (v.exclusiveClass === exclusive)
|
|
123
|
+
{
|
|
124
|
+
v.exclusiveRelease(this.synth.currentSynthTime);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
// compute all modulators
|
|
129
|
+
computeModulators(voice, this.midiControllers);
|
|
130
|
+
// modulate sample offsets (these are not real time)
|
|
131
|
+
const cursorStartOffset = voice.modulatedGenerators[generatorTypes.startAddrsOffset] + voice.modulatedGenerators[generatorTypes.startAddrsCoarseOffset] * 32768;
|
|
132
|
+
const endOffset = voice.modulatedGenerators[generatorTypes.endAddrOffset] + voice.modulatedGenerators[generatorTypes.endAddrsCoarseOffset] * 32768;
|
|
133
|
+
const loopStartOffset = voice.modulatedGenerators[generatorTypes.startloopAddrsOffset] + voice.modulatedGenerators[generatorTypes.startloopAddrsCoarseOffset] * 32768;
|
|
134
|
+
const loopEndOffset = voice.modulatedGenerators[generatorTypes.endloopAddrsOffset] + voice.modulatedGenerators[generatorTypes.endloopAddrsCoarseOffset] * 32768;
|
|
135
|
+
const sm = voice.sample;
|
|
136
|
+
// apply them
|
|
137
|
+
const clamp = num => Math.max(0, Math.min(sm.sampleData.length - 1, num));
|
|
138
|
+
sm.cursor = clamp(sm.cursor + cursorStartOffset);
|
|
139
|
+
sm.end = clamp(sm.end + endOffset);
|
|
140
|
+
sm.loopStart = clamp(sm.loopStart + loopStartOffset);
|
|
141
|
+
sm.loopEnd = clamp(sm.loopEnd + loopEndOffset);
|
|
142
|
+
// swap loops if needed
|
|
143
|
+
if (sm.loopEnd < sm.loopStart)
|
|
144
|
+
{
|
|
145
|
+
const temp = sm.loopStart;
|
|
146
|
+
sm.loopStart = sm.loopEnd;
|
|
147
|
+
sm.loopEnd = temp;
|
|
148
|
+
}
|
|
149
|
+
if (sm.loopEnd - sm.loopStart < 1)
|
|
150
|
+
{
|
|
151
|
+
sm.loopingMode = 0;
|
|
152
|
+
sm.isLooping = false;
|
|
153
|
+
}
|
|
154
|
+
// set the current attenuation to target,
|
|
155
|
+
// as it's interpolated (we don't want 0 attenuation for even a split second)
|
|
156
|
+
voice.volumeEnvelope.attenuation = voice.volumeEnvelope.attenuationTargetGain;
|
|
157
|
+
// set initial pan to avoid split second changing from middle to the correct value
|
|
158
|
+
voice.currentPan = Math.max(-500, Math.min(500, voice.modulatedGenerators[generatorTypes.pan])); // -500 to 500
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
this.synth.totalVoicesAmount += voices.length;
|
|
162
|
+
// cap the voices
|
|
163
|
+
if (this.synth.totalVoicesAmount > this.synth.voiceCap)
|
|
164
|
+
{
|
|
165
|
+
this.synth.voiceKilling(voices.length);
|
|
166
|
+
}
|
|
167
|
+
channelVoices.push(...voices);
|
|
168
|
+
this.sendChannelProperty();
|
|
169
|
+
this.synth.callEvent("noteon", {
|
|
170
|
+
midiNote: midiNote,
|
|
171
|
+
channel: this.channelNumber,
|
|
172
|
+
velocity: velocity
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// Tests were performed by John Novak
|
|
2
|
+
// https://github.com/dosbox-staging/dosbox-staging/pull/2705
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
CC 5 value Portamento time
|
|
6
|
+
---------- ---------------
|
|
7
|
+
0 0.000 s
|
|
8
|
+
1 0.006 s
|
|
9
|
+
2 0.023 s
|
|
10
|
+
4 0.050 s
|
|
11
|
+
8 0.110 s
|
|
12
|
+
16 0.250 s
|
|
13
|
+
32 0.500 s
|
|
14
|
+
64 2.060 s
|
|
15
|
+
80 4.200 s
|
|
16
|
+
96 8.400 s
|
|
17
|
+
112 19.500 s
|
|
18
|
+
116 26.700 s
|
|
19
|
+
120 40.000 s
|
|
20
|
+
124 80.000 s
|
|
21
|
+
127 480.000 s
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const portamentoLookup = {
|
|
25
|
+
0: 0.000,
|
|
26
|
+
1: 0.006,
|
|
27
|
+
2: 0.023,
|
|
28
|
+
4: 0.050,
|
|
29
|
+
8: 0.110,
|
|
30
|
+
16: 0.250,
|
|
31
|
+
32: 0.500,
|
|
32
|
+
64: 2.060,
|
|
33
|
+
80: 4.200,
|
|
34
|
+
96: 8.400,
|
|
35
|
+
112: 19.500,
|
|
36
|
+
116: 26.700,
|
|
37
|
+
120: 40.000,
|
|
38
|
+
124: 80.000,
|
|
39
|
+
127: 480.000
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param value {number}
|
|
44
|
+
* @returns {number}
|
|
45
|
+
*/
|
|
46
|
+
function getLookup(value)
|
|
47
|
+
{
|
|
48
|
+
if (portamentoLookup[value] !== undefined)
|
|
49
|
+
{
|
|
50
|
+
return portamentoLookup[value];
|
|
51
|
+
}
|
|
52
|
+
// get the nearest lower and upper points from the lookup table
|
|
53
|
+
let lower = null;
|
|
54
|
+
let upper = null;
|
|
55
|
+
|
|
56
|
+
for (let key of Object.keys(portamentoLookup))
|
|
57
|
+
{
|
|
58
|
+
key = parseInt(key);
|
|
59
|
+
if (key < value && (lower === null || key > lower))
|
|
60
|
+
{
|
|
61
|
+
lower = key;
|
|
62
|
+
}
|
|
63
|
+
if (key > value && (upper === null || key < upper))
|
|
64
|
+
{
|
|
65
|
+
upper = key;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// if we have found both lower and upper points, perform linear interpolation
|
|
70
|
+
if (lower !== null && upper !== null)
|
|
71
|
+
{
|
|
72
|
+
let lowerTime = portamentoLookup[lower];
|
|
73
|
+
let upperTime = portamentoLookup[upper];
|
|
74
|
+
|
|
75
|
+
// linear interpolation
|
|
76
|
+
return lowerTime + ((value - lower) * (upperTime - lowerTime)) / (upper - lower);
|
|
77
|
+
}
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Converts portamento time to seconds
|
|
84
|
+
* @param time {number} 0 - 127
|
|
85
|
+
* @param distance {number} distance in keys
|
|
86
|
+
* @returns {number} seconds
|
|
87
|
+
*/
|
|
88
|
+
export function portamentoTimeToSeconds(time, distance)
|
|
89
|
+
{
|
|
90
|
+
// this seems to work fine for the MIDIs I have
|
|
91
|
+
return getLookup(time) * (distance / 30);
|
|
92
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* executes a program change
|
|
3
|
+
* @param programNumber {number}
|
|
4
|
+
* @this {MidiAudioChannel}
|
|
5
|
+
*/
|
|
6
|
+
export function programChange(programNumber)
|
|
7
|
+
{
|
|
8
|
+
if (this.lockPreset)
|
|
9
|
+
{
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
// always 128 for percussion
|
|
13
|
+
let bank = this.getBankSelect();
|
|
14
|
+
let sentBank;
|
|
15
|
+
/**
|
|
16
|
+
* @type {BasicPreset}
|
|
17
|
+
*/
|
|
18
|
+
let preset;
|
|
19
|
+
|
|
20
|
+
const isXG = this.isXGChannel;
|
|
21
|
+
// check if override
|
|
22
|
+
if (this.synth.overrideSoundfont)
|
|
23
|
+
{
|
|
24
|
+
const bankWithOffset = bank === 128 ? 128 : bank - this.synth.soundfontBankOffset;
|
|
25
|
+
const p = this.synth.overrideSoundfont.getPresetNoFallback(
|
|
26
|
+
bankWithOffset,
|
|
27
|
+
programNumber,
|
|
28
|
+
isXG
|
|
29
|
+
);
|
|
30
|
+
if (p)
|
|
31
|
+
{
|
|
32
|
+
sentBank = p.bank === 128 ? 128 : p.bank + this.synth.soundfontBankOffset;
|
|
33
|
+
preset = p;
|
|
34
|
+
this.presetUsesOverride = true;
|
|
35
|
+
}
|
|
36
|
+
else
|
|
37
|
+
{
|
|
38
|
+
preset = this.synth.soundfontManager.getPreset(bank, programNumber, isXG);
|
|
39
|
+
const offset = this.synth.soundfontManager.soundfontList
|
|
40
|
+
.find(s => s.soundfont === preset.parentSoundBank).bankOffset;
|
|
41
|
+
sentBank = preset.bank - offset;
|
|
42
|
+
this.presetUsesOverride = false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else
|
|
46
|
+
{
|
|
47
|
+
preset = this.synth.soundfontManager.getPreset(bank, programNumber, isXG);
|
|
48
|
+
const offset = this.synth.soundfontManager.soundfontList
|
|
49
|
+
.find(s => s.soundfont === preset.parentSoundBank).bankOffset;
|
|
50
|
+
sentBank = preset.bank - offset;
|
|
51
|
+
this.presetUsesOverride = false;
|
|
52
|
+
}
|
|
53
|
+
this.setPreset(preset);
|
|
54
|
+
this.sentBank = sentBank;
|
|
55
|
+
this.synth.callEvent("programchange", {
|
|
56
|
+
channel: this.channelNumber,
|
|
57
|
+
program: preset.program,
|
|
58
|
+
bank: sentBank
|
|
59
|
+
});
|
|
60
|
+
this.sendChannelProperty();
|
|
61
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { VolumeEnvelope } from "../engine_components/volume_envelope.js";
|
|
2
|
+
import { ModulationEnvelope } from "../engine_components/modulation_envelope.js";
|
|
3
|
+
import { generatorTypes } from "../../../soundfont/basic_soundfont/generator.js";
|
|
4
|
+
import { customControllers } from "../engine_components/controller_tables.js";
|
|
5
|
+
import { absCentsToHz, timecentsToSeconds } from "../engine_components/unit_converter.js";
|
|
6
|
+
import { getLFOValue } from "../engine_components/lfo.js";
|
|
7
|
+
import { interpolationTypes, WavetableOscillator } from "../engine_components/wavetable_oscillator.js";
|
|
8
|
+
import { WorkletLowpassFilter } from "../engine_components/lowpass_filter.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Renders a voice to the stereo output buffer
|
|
12
|
+
* @param voice {Voice} the voice to render
|
|
13
|
+
* @param timeNow {number} current time in seconds
|
|
14
|
+
* @param outputLeft {Float32Array} the left output buffer
|
|
15
|
+
* @param outputRight {Float32Array} the right output buffer
|
|
16
|
+
* @param reverbOutputLeft {Float32Array} left output for reverb
|
|
17
|
+
* @param reverbOutputRight {Float32Array} right output for reverb
|
|
18
|
+
* @param chorusOutputLeft {Float32Array} left output for chorus
|
|
19
|
+
* @param chorusOutputRight {Float32Array} right output for chorus
|
|
20
|
+
* @this {MidiAudioChannel}
|
|
21
|
+
* @returns {boolean} true if the voice is finished
|
|
22
|
+
*/
|
|
23
|
+
export function renderVoice(
|
|
24
|
+
voice, timeNow,
|
|
25
|
+
outputLeft, outputRight,
|
|
26
|
+
reverbOutputLeft, reverbOutputRight,
|
|
27
|
+
chorusOutputLeft, chorusOutputRight
|
|
28
|
+
)
|
|
29
|
+
{
|
|
30
|
+
// check if release
|
|
31
|
+
if (!voice.isInRelease)
|
|
32
|
+
{
|
|
33
|
+
// if not in release, check if the release time is
|
|
34
|
+
if (timeNow >= voice.releaseStartTime)
|
|
35
|
+
{
|
|
36
|
+
// release the voice here
|
|
37
|
+
voice.isInRelease = true;
|
|
38
|
+
VolumeEnvelope.startRelease(voice);
|
|
39
|
+
ModulationEnvelope.startRelease(voice);
|
|
40
|
+
if (voice.sample.loopingMode === 3)
|
|
41
|
+
{
|
|
42
|
+
voice.sample.isLooping = false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
// if the initial attenuation is more than 100dB, skip the voice (it's silent anyway)
|
|
49
|
+
if (voice.modulatedGenerators[generatorTypes.initialAttenuation] > 2500)
|
|
50
|
+
{
|
|
51
|
+
if (voice.isInRelease)
|
|
52
|
+
{
|
|
53
|
+
voice.finished = true;
|
|
54
|
+
}
|
|
55
|
+
return voice.finished;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// TUNING
|
|
59
|
+
let targetKey = voice.targetKey;
|
|
60
|
+
|
|
61
|
+
// calculate tuning
|
|
62
|
+
let cents = voice.modulatedGenerators[generatorTypes.fineTune] // soundfont fine tune
|
|
63
|
+
+ this.channelOctaveTuning[voice.midiNote] // MTS octave tuning
|
|
64
|
+
+ this.channelTuningCents; // channel tuning
|
|
65
|
+
let semitones = voice.modulatedGenerators[generatorTypes.coarseTune]; // soundfont coarse tuning
|
|
66
|
+
|
|
67
|
+
// midi tuning standard
|
|
68
|
+
const tuning = this.synth.tunings[this.preset.program]?.[voice.realKey];
|
|
69
|
+
if (tuning !== undefined && tuning?.midiNote >= 0)
|
|
70
|
+
{
|
|
71
|
+
// override key
|
|
72
|
+
targetKey = tuning.midiNote;
|
|
73
|
+
// add micro-tonal tuning
|
|
74
|
+
cents += tuning.centTuning;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// portamento
|
|
78
|
+
if (voice.portamentoFromKey > -1)
|
|
79
|
+
{
|
|
80
|
+
// 0 to 1
|
|
81
|
+
const elapsed = Math.min((timeNow - voice.startTime) / voice.portamentoDuration, 1);
|
|
82
|
+
const diff = targetKey - voice.portamentoFromKey;
|
|
83
|
+
// zero progress means the pitch being in fromKey, full progress means the normal pitch
|
|
84
|
+
semitones -= diff * (1 - elapsed);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// calculate tuning by key using soundfont's scale tuning
|
|
88
|
+
cents += (targetKey - voice.sample.rootKey) * voice.modulatedGenerators[generatorTypes.scaleTuning];
|
|
89
|
+
|
|
90
|
+
// vibrato LFO
|
|
91
|
+
const vibratoDepth = voice.modulatedGenerators[generatorTypes.vibLfoToPitch];
|
|
92
|
+
if (vibratoDepth !== 0)
|
|
93
|
+
{
|
|
94
|
+
// calculate start time and lfo value
|
|
95
|
+
const vibStart = voice.startTime + timecentsToSeconds(voice.modulatedGenerators[generatorTypes.delayVibLFO]);
|
|
96
|
+
const vibFreqHz = absCentsToHz(voice.modulatedGenerators[generatorTypes.freqVibLFO]);
|
|
97
|
+
const lfoVal = getLFOValue(vibStart, vibFreqHz, timeNow);
|
|
98
|
+
// use modulation multiplier (RPN modulation depth)
|
|
99
|
+
cents += lfoVal * (vibratoDepth * this.customControllers[customControllers.modulationMultiplier]);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// low pass excursion with LFO and mod envelope
|
|
103
|
+
let lowpassExcursion = 0;
|
|
104
|
+
|
|
105
|
+
// mod LFO
|
|
106
|
+
const modPitchDepth = voice.modulatedGenerators[generatorTypes.modLfoToPitch];
|
|
107
|
+
const modVolDepth = voice.modulatedGenerators[generatorTypes.modLfoToVolume];
|
|
108
|
+
const modFilterDepth = voice.modulatedGenerators[generatorTypes.modLfoToFilterFc];
|
|
109
|
+
let modLfoCentibels = 0;
|
|
110
|
+
// don't compute mod lfo unless necessary
|
|
111
|
+
if (modPitchDepth !== 0 || modFilterDepth !== 0 || modVolDepth !== 0)
|
|
112
|
+
{
|
|
113
|
+
// calculate start time and lfo value
|
|
114
|
+
const modStart = voice.startTime + timecentsToSeconds(voice.modulatedGenerators[generatorTypes.delayModLFO]);
|
|
115
|
+
const modFreqHz = absCentsToHz(voice.modulatedGenerators[generatorTypes.freqModLFO]);
|
|
116
|
+
const modLfoValue = getLFOValue(modStart, modFreqHz, timeNow);
|
|
117
|
+
// use modulation multiplier (RPN modulation depth)
|
|
118
|
+
cents += modLfoValue * (modPitchDepth * this.customControllers[customControllers.modulationMultiplier]);
|
|
119
|
+
// vole nv volume offset
|
|
120
|
+
// negate the lfo value because audigy starts with increase rather than decrease
|
|
121
|
+
modLfoCentibels = -modLfoValue * modVolDepth;
|
|
122
|
+
// low pass frequency
|
|
123
|
+
lowpassExcursion += modLfoValue * modFilterDepth;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// channel vibrato (GS NRPN)
|
|
127
|
+
if (this.channelVibrato.depth > 0)
|
|
128
|
+
{
|
|
129
|
+
// same as others
|
|
130
|
+
const channelVibrato = getLFOValue(
|
|
131
|
+
voice.startTime + this.channelVibrato.delay,
|
|
132
|
+
this.channelVibrato.rate,
|
|
133
|
+
timeNow
|
|
134
|
+
);
|
|
135
|
+
if (channelVibrato)
|
|
136
|
+
{
|
|
137
|
+
cents += channelVibrato * this.channelVibrato.depth;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// mod env
|
|
142
|
+
const modEnvPitchDepth = voice.modulatedGenerators[generatorTypes.modEnvToPitch];
|
|
143
|
+
const modEnvFilterDepth = voice.modulatedGenerators[generatorTypes.modEnvToFilterFc];
|
|
144
|
+
// don't compute mod env unless necessary
|
|
145
|
+
if (modEnvFilterDepth !== 0 || modEnvPitchDepth !== 0)
|
|
146
|
+
{
|
|
147
|
+
const modEnv = ModulationEnvelope.getValue(voice, timeNow);
|
|
148
|
+
// apply values
|
|
149
|
+
lowpassExcursion += modEnv * modEnvFilterDepth;
|
|
150
|
+
cents += modEnv * modEnvPitchDepth;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// finally, calculate the playback rate
|
|
154
|
+
const centsTotal = ~~(cents + semitones * 100);
|
|
155
|
+
if (centsTotal !== voice.currentTuningCents)
|
|
156
|
+
{
|
|
157
|
+
voice.currentTuningCents = centsTotal;
|
|
158
|
+
voice.currentTuningCalculated = Math.pow(2, centsTotal / 1200);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
// SYNTHESIS
|
|
163
|
+
const bufferOut = new Float32Array(outputLeft.length);
|
|
164
|
+
|
|
165
|
+
// wave table oscillator
|
|
166
|
+
switch (this.synth.interpolationType)
|
|
167
|
+
{
|
|
168
|
+
case interpolationTypes.fourthOrder:
|
|
169
|
+
WavetableOscillator.getSampleCubic(voice, bufferOut);
|
|
170
|
+
break;
|
|
171
|
+
|
|
172
|
+
case interpolationTypes.linear:
|
|
173
|
+
default:
|
|
174
|
+
WavetableOscillator.getSampleLinear(voice, bufferOut);
|
|
175
|
+
break;
|
|
176
|
+
|
|
177
|
+
case interpolationTypes.nearestNeighbor:
|
|
178
|
+
WavetableOscillator.getSampleNearest(voice, bufferOut);
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// low pass filter
|
|
183
|
+
WorkletLowpassFilter.apply(voice, bufferOut, lowpassExcursion, this.synth.filterSmoothingFactor);
|
|
184
|
+
|
|
185
|
+
// vol env
|
|
186
|
+
VolumeEnvelope.apply(voice, bufferOut, modLfoCentibels, this.synth.volumeEnvelopeSmoothingFactor);
|
|
187
|
+
|
|
188
|
+
this.panVoice(
|
|
189
|
+
voice,
|
|
190
|
+
bufferOut,
|
|
191
|
+
outputLeft, outputRight,
|
|
192
|
+
reverbOutputLeft, reverbOutputRight,
|
|
193
|
+
chorusOutputLeft, chorusOutputRight
|
|
194
|
+
);
|
|
195
|
+
return voice.finished;
|
|
196
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @this {SpessaSynthProcessor}
|
|
3
|
+
* @param sendPresets {boolean}
|
|
4
|
+
* @param clearOverride {boolean}
|
|
5
|
+
*/
|
|
6
|
+
export function clearSoundFont(sendPresets = true, clearOverride = true)
|
|
7
|
+
{
|
|
8
|
+
this.stopAllChannels(true);
|
|
9
|
+
if (clearOverride)
|
|
10
|
+
{
|
|
11
|
+
delete this.overrideSoundfont;
|
|
12
|
+
this.overrideSoundfont = undefined;
|
|
13
|
+
}
|
|
14
|
+
this.getDefaultPresets();
|
|
15
|
+
this.cachedVoices = [];
|
|
16
|
+
|
|
17
|
+
for (let i = 0; i < this.midiAudioChannels.length; i++)
|
|
18
|
+
{
|
|
19
|
+
const channelObject = this.midiAudioChannels[i];
|
|
20
|
+
if (!clearOverride || (clearOverride && channelObject.presetUsesOverride))
|
|
21
|
+
{
|
|
22
|
+
channelObject.setPresetLock(false);
|
|
23
|
+
}
|
|
24
|
+
channelObject.programChange(channelObject.preset.program);
|
|
25
|
+
}
|
|
26
|
+
if (sendPresets)
|
|
27
|
+
{
|
|
28
|
+
this.sendPresetList();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { isSystemXG } from "../../../../utils/xg_hacks.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @this {SpessaSynthProcessor}
|
|
5
|
+
* @param program {number}
|
|
6
|
+
* @param bank {number}
|
|
7
|
+
* @returns {BasicPreset}
|
|
8
|
+
*/
|
|
9
|
+
export function getPreset(bank, program)
|
|
10
|
+
{
|
|
11
|
+
if (this.overrideSoundfont)
|
|
12
|
+
{
|
|
13
|
+
// if override soundfont
|
|
14
|
+
const bankWithOffset = bank === 128 ? 128 : bank - this.soundfontBankOffset;
|
|
15
|
+
const preset = this.overrideSoundfont.getPresetNoFallback(bankWithOffset, program, isSystemXG(this.system));
|
|
16
|
+
if (preset)
|
|
17
|
+
{
|
|
18
|
+
return preset;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return this.soundfontManager.getPreset(bank, program, isSystemXG(this.system));
|
|
22
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { loadSoundFont } from "../../../../soundfont/load_soundfont.js";
|
|
2
|
+
import { SpessaSynthInfo } from "../../../../utils/loggin.js";
|
|
3
|
+
import { consoleColors } from "../../../../utils/other.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param buffer {ArrayBuffer}
|
|
7
|
+
* @param isOverride {Boolean}
|
|
8
|
+
* @this {SpessaSynthProcessor}
|
|
9
|
+
*/
|
|
10
|
+
export function reloadSoundFont(buffer, isOverride = false)
|
|
11
|
+
{
|
|
12
|
+
this.clearSoundFont(false, isOverride);
|
|
13
|
+
if (isOverride)
|
|
14
|
+
{
|
|
15
|
+
this.overrideSoundfont = loadSoundFont(buffer);
|
|
16
|
+
}
|
|
17
|
+
else
|
|
18
|
+
{
|
|
19
|
+
this.soundfontManager.reloadManager(buffer);
|
|
20
|
+
}
|
|
21
|
+
this.getDefaultPresets();
|
|
22
|
+
this.midiAudioChannels.forEach(c =>
|
|
23
|
+
c.programChange(c.preset.program)
|
|
24
|
+
);
|
|
25
|
+
this.postReady();
|
|
26
|
+
this.sendPresetList();
|
|
27
|
+
SpessaSynthInfo("%cSpessaSynth is ready!", consoleColors.recognized);
|
|
28
|
+
}
|