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
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
import { consoleColors } from '../../../utils/other.js'
|
|
2
|
-
import { midiControllers } from '../../../midi_parser/midi_message.js'
|
|
3
|
-
import { DEFAULT_PERCUSSION, DEFAULT_SYNTH_MODE } from '../../synthesizer.js'
|
|
4
|
-
import {
|
|
5
|
-
customControllers,
|
|
6
|
-
customResetArray,
|
|
7
|
-
dataEntryStates,
|
|
8
|
-
resetArray,
|
|
9
|
-
} from '../worklet_utilities/worklet_processor_channel.js'
|
|
10
|
-
import { computeModulators } from '../worklet_utilities/worklet_modulator.js'
|
|
11
|
-
import { SpessaSynthInfo } from '../../../utils/loggin.js'
|
|
12
|
-
import { SYNTHESIZER_GAIN } from '../../synthesizer.js'
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Changes a MIDI controller
|
|
16
|
-
* @param channel {number} - The MIDI Channel to use
|
|
17
|
-
* @param controllerNumber {number} - The MIDI controller number
|
|
18
|
-
* @param controllerValue {number} - The new value
|
|
19
|
-
* @this {Synthesizer}
|
|
20
|
-
*/
|
|
21
|
-
export function controllerChange(channel, controllerNumber, controllerValue)
|
|
22
|
-
{
|
|
23
|
-
/**
|
|
24
|
-
* @type {WorkletProcessorChannel}
|
|
25
|
-
*/
|
|
26
|
-
const channelObject = this.workletProcessorChannels[channel];
|
|
27
|
-
switch (controllerNumber) {
|
|
28
|
-
case midiControllers.allNotesOff:
|
|
29
|
-
this.stopAll(channel);
|
|
30
|
-
break;
|
|
31
|
-
|
|
32
|
-
case midiControllers.allSoundOff:
|
|
33
|
-
this.stopAll(channel, true);
|
|
34
|
-
break;
|
|
35
|
-
|
|
36
|
-
case midiControllers.bankSelect:
|
|
37
|
-
let bankNr = controllerValue;
|
|
38
|
-
switch (this.system)
|
|
39
|
-
{
|
|
40
|
-
case "gm":
|
|
41
|
-
// gm ignores bank select
|
|
42
|
-
SpessaSynthInfo(`%cIgnoring the Bank Select (${controllerValue}), as the synth is in GM mode.`, consoleColors.info);
|
|
43
|
-
return;
|
|
44
|
-
|
|
45
|
-
case "xg":
|
|
46
|
-
// for xg, if msb is 127, then it's drums
|
|
47
|
-
if (bankNr === 127)
|
|
48
|
-
{
|
|
49
|
-
channelObject.drumChannel = true;
|
|
50
|
-
}
|
|
51
|
-
break;
|
|
52
|
-
|
|
53
|
-
case "gm2":
|
|
54
|
-
if(bankNr === 120)
|
|
55
|
-
{
|
|
56
|
-
channelObject.drumChannel = true;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if(channelObject.drumChannel)
|
|
61
|
-
{
|
|
62
|
-
// 128 for percussion channel
|
|
63
|
-
bankNr = 128;
|
|
64
|
-
}
|
|
65
|
-
if(bankNr === 128 && !channelObject.drumChannel)
|
|
66
|
-
{
|
|
67
|
-
// if channel is not for percussion, default to bank current
|
|
68
|
-
bankNr = channelObject.midiControllers[midiControllers.bankSelect];
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
channelObject.midiControllers[midiControllers.bankSelect] = bankNr;
|
|
72
|
-
break;
|
|
73
|
-
|
|
74
|
-
case midiControllers.lsbForControl0BankSelect:
|
|
75
|
-
if(this.system === 'xg')
|
|
76
|
-
{
|
|
77
|
-
if(channelObject.midiControllers[midiControllers.bankSelect] === 0)
|
|
78
|
-
{
|
|
79
|
-
if(controllerValue !== 127)
|
|
80
|
-
channelObject.midiControllers[midiControllers.bankSelect] = controllerValue;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
else
|
|
84
|
-
if(this.system === "gm2")
|
|
85
|
-
{
|
|
86
|
-
channelObject.midiControllers[midiControllers.bankSelect] = controllerValue;
|
|
87
|
-
}
|
|
88
|
-
break;
|
|
89
|
-
|
|
90
|
-
case midiControllers.RPNLsb:
|
|
91
|
-
channelObject.RPValue = channelObject.RPValue << 7 | controllerValue;
|
|
92
|
-
channelObject.dataEntryState = dataEntryStates.RPFine;
|
|
93
|
-
break;
|
|
94
|
-
|
|
95
|
-
case midiControllers.RPNMsb:
|
|
96
|
-
channelObject.RPValue = controllerValue;
|
|
97
|
-
channelObject.dataEntryState = dataEntryStates.RPCoarse;
|
|
98
|
-
break;
|
|
99
|
-
|
|
100
|
-
case midiControllers.NRPNMsb:
|
|
101
|
-
channelObject.NRPCoarse = controllerValue;
|
|
102
|
-
channelObject.dataEntryState = dataEntryStates.NRPCoarse;
|
|
103
|
-
break;
|
|
104
|
-
|
|
105
|
-
case midiControllers.NRPNLsb:
|
|
106
|
-
channelObject.NRPFine = controllerValue;
|
|
107
|
-
channelObject.dataEntryState = dataEntryStates.NRPFine;
|
|
108
|
-
break;
|
|
109
|
-
|
|
110
|
-
case midiControllers.dataEntryMsb:
|
|
111
|
-
this.dataEntryCoarse(channel, controllerValue);
|
|
112
|
-
break;
|
|
113
|
-
|
|
114
|
-
case midiControllers.lsbForControl6DataEntry:
|
|
115
|
-
this.dataEntryFine(channel, controllerValue);
|
|
116
|
-
break;
|
|
117
|
-
|
|
118
|
-
default:
|
|
119
|
-
if(channelObject.lockedControllers[controllerNumber])
|
|
120
|
-
{
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
// special case: hold pedal
|
|
124
|
-
if(controllerNumber === midiControllers.sustainPedal) {
|
|
125
|
-
if (controllerValue >= 64)
|
|
126
|
-
{
|
|
127
|
-
channelObject.holdPedal = true;
|
|
128
|
-
}
|
|
129
|
-
else
|
|
130
|
-
{
|
|
131
|
-
channelObject.holdPedal = false;
|
|
132
|
-
channelObject.sustainedVoices.forEach(v => {
|
|
133
|
-
this.releaseVoice(v)
|
|
134
|
-
});
|
|
135
|
-
channelObject.sustainedVoices = [];
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
channelObject.midiControllers[controllerNumber] = controllerValue << 7;
|
|
139
|
-
channelObject.voices.forEach(v => computeModulators(v, channelObject.midiControllers));
|
|
140
|
-
break;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Resets all controllers and programs
|
|
146
|
-
* @this {Synthesizer}
|
|
147
|
-
*/
|
|
148
|
-
export function resetAllControllers()
|
|
149
|
-
{
|
|
150
|
-
SpessaSynthInfo("%cResetting all controllers!", consoleColors.info);
|
|
151
|
-
for (let channelNumber = 0; channelNumber < this.workletProcessorChannels.length; channelNumber++)
|
|
152
|
-
{
|
|
153
|
-
this.resetControllers(channelNumber);
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* @type {WorkletProcessorChannel}
|
|
157
|
-
**/
|
|
158
|
-
const ch = this.workletProcessorChannels[channelNumber];
|
|
159
|
-
|
|
160
|
-
// if preset is unlocked, switch to non drums and call event
|
|
161
|
-
if(!ch.lockPreset)
|
|
162
|
-
{
|
|
163
|
-
ch.midiControllers[midiControllers.bankSelect] = 0;
|
|
164
|
-
if (channelNumber % 16 === DEFAULT_PERCUSSION)
|
|
165
|
-
{
|
|
166
|
-
this.setPreset(channelNumber, this.drumPreset);
|
|
167
|
-
ch.drumChannel = true;
|
|
168
|
-
}
|
|
169
|
-
else
|
|
170
|
-
{
|
|
171
|
-
ch.drumChannel = false;
|
|
172
|
-
this.setPreset(channelNumber, this.defaultPreset);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
this.system = DEFAULT_SYNTH_MODE;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Resets all controllers for channel
|
|
181
|
-
* @param channel {number} - The MIDI Channel to use
|
|
182
|
-
* @this {Synthesizer}
|
|
183
|
-
*/
|
|
184
|
-
export function resetControllers(channel)
|
|
185
|
-
{
|
|
186
|
-
const channelObject = this.workletProcessorChannels[channel];
|
|
187
|
-
/**
|
|
188
|
-
* get excluded (locked) cc numbers as locked ccs are unaffected by reset
|
|
189
|
-
* @type {number[]}
|
|
190
|
-
*/
|
|
191
|
-
const excludedCCs = channelObject.lockedControllers.reduce((lockedCCs, cc, ccNum) => {
|
|
192
|
-
if(cc)
|
|
193
|
-
{
|
|
194
|
-
lockedCCs.push(ccNum);
|
|
195
|
-
}
|
|
196
|
-
return lockedCCs;
|
|
197
|
-
}, []);
|
|
198
|
-
// save excluded controllers as reset doesn't affect them
|
|
199
|
-
let excludedCCvalues = excludedCCs.map(ccNum => {
|
|
200
|
-
return {
|
|
201
|
-
ccNum: ccNum,
|
|
202
|
-
ccVal: channelObject.midiControllers[ccNum]
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
// reset the array
|
|
207
|
-
channelObject.midiControllers.set(resetArray);
|
|
208
|
-
channelObject.channelVibrato = {rate: 0, depth: 0, delay: 0};
|
|
209
|
-
channelObject.holdPedal = false;
|
|
210
|
-
|
|
211
|
-
excludedCCvalues.forEach((cc) => {
|
|
212
|
-
channelObject.midiControllers[cc.ccNum] = cc.ccVal;
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
// reset custom controllers
|
|
216
|
-
// special case: transpose does not get affected
|
|
217
|
-
const transpose = channelObject.customControllers[customControllers.channelTranspose];
|
|
218
|
-
channelObject.customControllers.set(customResetArray);
|
|
219
|
-
channelObject.customControllers[customControllers.channelTranspose] = transpose;
|
|
220
|
-
|
|
221
|
-
this.resetParameters(channel);
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* @param channel {number}
|
|
227
|
-
* @this {Synthesizer}
|
|
228
|
-
*/
|
|
229
|
-
export function resetParameters(channel)
|
|
230
|
-
{
|
|
231
|
-
const channelObject = this.workletProcessorChannels[channel];
|
|
232
|
-
|
|
233
|
-
// reset parameters
|
|
234
|
-
/**
|
|
235
|
-
* @type {number}
|
|
236
|
-
*/
|
|
237
|
-
channelObject.NRPCoarse = 0;
|
|
238
|
-
/**
|
|
239
|
-
* @type {number}
|
|
240
|
-
*/
|
|
241
|
-
channelObject.NRPFine = 0;
|
|
242
|
-
/**
|
|
243
|
-
* @type {number}
|
|
244
|
-
*/
|
|
245
|
-
channelObject.RPValue = 0;
|
|
246
|
-
/**
|
|
247
|
-
* @type {string}
|
|
248
|
-
*/
|
|
249
|
-
channelObject.dataEntryState = dataEntryStates.Idle;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Sets the main volume of the synthesizer
|
|
254
|
-
* @param volume {number} - 0-1 the volume
|
|
255
|
-
* @this {Synthesizer}
|
|
256
|
-
*/
|
|
257
|
-
export function setMainVolume(volume)
|
|
258
|
-
{
|
|
259
|
-
this.mainVolume = volume * SYNTHESIZER_GAIN;
|
|
260
|
-
this.setMasterPan(this.pan);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Sets the master stereo panning of the synthesizer
|
|
265
|
-
* @param pan {number} - -1 to 1 the pan, -1 is left, 0 is the middle, 1 is right
|
|
266
|
-
* @this {Synthesizer}
|
|
267
|
-
*/
|
|
268
|
-
export function setMasterPan(pan)
|
|
269
|
-
{
|
|
270
|
-
this.pan = pan;
|
|
271
|
-
// clamp to 0-1 (0 is left)
|
|
272
|
-
pan = (pan / 2) + 0.5;
|
|
273
|
-
this.panLeft = (1 - pan) * this.mainVolume;
|
|
274
|
-
this.panRight = (pan) * this.mainVolume;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Mutes/unmutes a channel
|
|
279
|
-
* @param channel {number} - The MIDI channel to use
|
|
280
|
-
* @param isMuted {boolean} - If the channel should be muted or not
|
|
281
|
-
* @this {Synthesizer}
|
|
282
|
-
*/
|
|
283
|
-
export function muteChannel(channel, isMuted)
|
|
284
|
-
{
|
|
285
|
-
if(isMuted)
|
|
286
|
-
{
|
|
287
|
-
this.stopAll(channel, true);
|
|
288
|
-
}
|
|
289
|
-
this.workletProcessorChannels[channel].isMuted = isMuted;
|
|
290
|
-
}
|
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
import { consoleColors } from '../../../utils/other.js'
|
|
2
|
-
import { midiControllers } from '../../../midi_parser/midi_message.js'
|
|
3
|
-
import {
|
|
4
|
-
customControllers,
|
|
5
|
-
dataEntryStates,
|
|
6
|
-
NON_CC_INDEX_OFFSET,
|
|
7
|
-
} from '../worklet_utilities/worklet_processor_channel.js'
|
|
8
|
-
import { modulatorSources } from '../../../soundfont/chunk/modulators.js'
|
|
9
|
-
import { SpessaSynthInfo, SpessaSynthWarn } from '../../../utils/loggin.js'
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Executes a data entry for an NRP for a sc88pro NRP (because touhou yes) and RPN tuning
|
|
13
|
-
* @param channel {number}
|
|
14
|
-
* @param dataValue {number} dataEntryCoarse MSB
|
|
15
|
-
* @this {Synthesizer}
|
|
16
|
-
* @private
|
|
17
|
-
*/
|
|
18
|
-
export function dataEntryCoarse(channel, dataValue)
|
|
19
|
-
{
|
|
20
|
-
const channelObject = this.workletProcessorChannels[channel];
|
|
21
|
-
let addDefaultVibrato = () =>
|
|
22
|
-
{
|
|
23
|
-
if(channelObject.channelVibrato.delay === 0 && channelObject.channelVibrato.rate === 0 && channelObject.channelVibrato.depth === 0)
|
|
24
|
-
{
|
|
25
|
-
channelObject.channelVibrato.depth = 50;
|
|
26
|
-
channelObject.channelVibrato.rate = 8;
|
|
27
|
-
channelObject.channelVibrato.delay = 0.6;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
switch(channelObject.dataEntryState)
|
|
31
|
-
{
|
|
32
|
-
default:
|
|
33
|
-
case dataEntryStates.Idle:
|
|
34
|
-
break;
|
|
35
|
-
|
|
36
|
-
// https://cdn.roland.com/assets/media/pdf/SC-88PRO_OM.pdf
|
|
37
|
-
// http://hummer.stanford.edu/sig/doc/classes/MidiOutput/rpn.html
|
|
38
|
-
case dataEntryStates.NRPFine:
|
|
39
|
-
switch(channelObject.NRPCoarse)
|
|
40
|
-
{
|
|
41
|
-
default:
|
|
42
|
-
if(dataValue === 64)
|
|
43
|
-
{
|
|
44
|
-
// default value
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
SpessaSynthWarn(
|
|
48
|
-
`%cUnrecognized NRPN for %c${channel}%c: %c(0x${channelObject.NRPCoarse.toString(16).toUpperCase()} 0x${channelObject.NRPFine.toString(16).toUpperCase()})%c data value: %c${dataValue}`,
|
|
49
|
-
consoleColors.warn,
|
|
50
|
-
consoleColors.recognized,
|
|
51
|
-
consoleColors.warn,
|
|
52
|
-
consoleColors.unrecognized,
|
|
53
|
-
consoleColors.warn,
|
|
54
|
-
consoleColors.value);
|
|
55
|
-
break;
|
|
56
|
-
|
|
57
|
-
case 0x01:
|
|
58
|
-
switch(channelObject.NRPFine)
|
|
59
|
-
{
|
|
60
|
-
default:
|
|
61
|
-
if(dataValue === 64)
|
|
62
|
-
{
|
|
63
|
-
// default value
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
SpessaSynthWarn(
|
|
67
|
-
`%cUnrecognized NRPN for %c${channel}%c: %c(0x${channelObject.NRPCoarse.toString(16)} 0x${channelObject.NRPFine.toString(16)})%c data value: %c${dataValue}`,
|
|
68
|
-
consoleColors.warn,
|
|
69
|
-
consoleColors.recognized,
|
|
70
|
-
consoleColors.warn,
|
|
71
|
-
consoleColors.unrecognized,
|
|
72
|
-
consoleColors.warn,
|
|
73
|
-
consoleColors.value);
|
|
74
|
-
break;
|
|
75
|
-
|
|
76
|
-
// vibrato rate
|
|
77
|
-
case 0x08:
|
|
78
|
-
if(channelObject.lockVibrato)
|
|
79
|
-
{
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
if(dataValue === 64)
|
|
83
|
-
{
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
addDefaultVibrato();
|
|
87
|
-
channelObject.channelVibrato.rate = (dataValue / 64) * 8;
|
|
88
|
-
SpessaSynthInfo(`%cVibrato rate for channel %c${channel}%c is now set to %c${channelObject.channelVibrato.rate}%cHz.`,
|
|
89
|
-
consoleColors.info,
|
|
90
|
-
consoleColors.recognized,
|
|
91
|
-
consoleColors.info,
|
|
92
|
-
consoleColors.value,
|
|
93
|
-
consoleColors.info);
|
|
94
|
-
break;
|
|
95
|
-
|
|
96
|
-
// vibrato depth
|
|
97
|
-
case 0x09:
|
|
98
|
-
if(channelObject.lockVibrato)
|
|
99
|
-
{
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
if(dataValue === 64)
|
|
103
|
-
{
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
addDefaultVibrato();
|
|
107
|
-
channelObject.channelVibrato.depth = dataValue / 2;
|
|
108
|
-
SpessaSynthInfo(`%cVibrato depth for %c${channel}%c is now set to %c${channelObject.channelVibrato.depth}%c cents range of detune.`,
|
|
109
|
-
consoleColors.info,
|
|
110
|
-
consoleColors.recognized,
|
|
111
|
-
consoleColors.info,
|
|
112
|
-
consoleColors.value,
|
|
113
|
-
consoleColors.info);
|
|
114
|
-
break;
|
|
115
|
-
|
|
116
|
-
// vibrato delay
|
|
117
|
-
case 0x0A:
|
|
118
|
-
if(channelObject.lockVibrato)
|
|
119
|
-
{
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
if(dataValue === 64)
|
|
123
|
-
{
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
addDefaultVibrato();
|
|
127
|
-
channelObject.channelVibrato.delay = (dataValue / 64) / 3;
|
|
128
|
-
SpessaSynthInfo(`%cVibrato delay for %c${channel}%c is now set to %c${channelObject.channelVibrato.delay}%c seconds.`,
|
|
129
|
-
consoleColors.info,
|
|
130
|
-
consoleColors.recognized,
|
|
131
|
-
consoleColors.info,
|
|
132
|
-
consoleColors.value,
|
|
133
|
-
consoleColors.info);
|
|
134
|
-
break;
|
|
135
|
-
|
|
136
|
-
// filter cutoff
|
|
137
|
-
case 0x20:
|
|
138
|
-
// affect the "brightness" controller as we have a default modulator that controls it
|
|
139
|
-
const ccValue = dataValue;
|
|
140
|
-
this.controllerChange(channel, midiControllers.brightness, dataValue)
|
|
141
|
-
SpessaSynthInfo(`%cFilter cutoff for %c${channel}%c is now set to %c${ccValue}`,
|
|
142
|
-
consoleColors.info,
|
|
143
|
-
consoleColors.recognized,
|
|
144
|
-
consoleColors.info,
|
|
145
|
-
consoleColors.value);
|
|
146
|
-
}
|
|
147
|
-
break;
|
|
148
|
-
|
|
149
|
-
// drum reverb
|
|
150
|
-
case 0x1D:
|
|
151
|
-
if(!channelObject.percussionChannel)
|
|
152
|
-
{
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
const reverb = dataValue;
|
|
156
|
-
this.controllerChange(channel, midiControllers.effects1Depth, reverb);
|
|
157
|
-
SpessaSynthInfo(
|
|
158
|
-
`%cGS Drum reverb for %c${channel}%c: %c${reverb}`,
|
|
159
|
-
consoleColors.info,
|
|
160
|
-
consoleColors.recognized,
|
|
161
|
-
consoleColors.info,
|
|
162
|
-
consoleColors.value);
|
|
163
|
-
break;
|
|
164
|
-
|
|
165
|
-
// drum chorus
|
|
166
|
-
case 0x1E:
|
|
167
|
-
if(!channelObject.percussionChannel)
|
|
168
|
-
{
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
const chorus = dataValue;
|
|
172
|
-
this.controllerChange(channel, midiControllers.effects3Depth, chorus);
|
|
173
|
-
SpessaSynthInfo(
|
|
174
|
-
`%cGS Drum chorus for %c${channel}%c: %c${chorus}`,
|
|
175
|
-
consoleColors.info,
|
|
176
|
-
consoleColors.recognized,
|
|
177
|
-
consoleColors.info,
|
|
178
|
-
consoleColors.value);
|
|
179
|
-
}
|
|
180
|
-
break;
|
|
181
|
-
|
|
182
|
-
case dataEntryStates.RPCoarse:
|
|
183
|
-
case dataEntryStates.RPFine:
|
|
184
|
-
switch(channelObject.RPValue)
|
|
185
|
-
{
|
|
186
|
-
default:
|
|
187
|
-
SpessaSynthWarn(
|
|
188
|
-
`%cUnrecognized RPN for %c${channel}%c: %c(0x${channelObject.RPValue.toString(16)})%c data value: %c${dataValue}`,
|
|
189
|
-
consoleColors.warn,
|
|
190
|
-
consoleColors.recognized,
|
|
191
|
-
consoleColors.warn,
|
|
192
|
-
consoleColors.unrecognized,
|
|
193
|
-
consoleColors.warn,
|
|
194
|
-
consoleColors.value);
|
|
195
|
-
break;
|
|
196
|
-
|
|
197
|
-
// pitch bend range
|
|
198
|
-
case 0x0000:
|
|
199
|
-
channelObject.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheelRange] = dataValue << 7;
|
|
200
|
-
SpessaSynthInfo(`%cChannel ${channel} bend range. Semitones: %c${dataValue}`,
|
|
201
|
-
consoleColors.info,
|
|
202
|
-
consoleColors.value);
|
|
203
|
-
break;
|
|
204
|
-
|
|
205
|
-
// coarse tuning
|
|
206
|
-
case 0x0002:
|
|
207
|
-
// semitones
|
|
208
|
-
this.setChannelTuning(channel, (dataValue - 64) * 100);
|
|
209
|
-
break;
|
|
210
|
-
|
|
211
|
-
// fine tuning
|
|
212
|
-
case 0x0001:
|
|
213
|
-
// note: this will not work properly unless the lsb is sent!
|
|
214
|
-
// here we store the raw value to then adjust in fine
|
|
215
|
-
this.setChannelTuning(channel, (dataValue - 64));
|
|
216
|
-
break;
|
|
217
|
-
|
|
218
|
-
// modulation depth
|
|
219
|
-
case 0x0005:
|
|
220
|
-
this.setModulationDepth(channel, dataValue * 100);
|
|
221
|
-
break
|
|
222
|
-
|
|
223
|
-
case 0x3FFF:
|
|
224
|
-
this.resetParameters(channel);
|
|
225
|
-
break;
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Executes a data entry for an RPN tuning
|
|
234
|
-
* @param channel {number}
|
|
235
|
-
* @param dataValue {number} dataEntry LSB
|
|
236
|
-
* @this {Synthesizer}
|
|
237
|
-
* @private
|
|
238
|
-
*/
|
|
239
|
-
export function dataEntryFine(channel, dataValue)
|
|
240
|
-
{
|
|
241
|
-
const channelObject = this.workletProcessorChannels[channel];
|
|
242
|
-
switch (channelObject.dataEntryState)
|
|
243
|
-
{
|
|
244
|
-
default:
|
|
245
|
-
break;
|
|
246
|
-
|
|
247
|
-
case dataEntryStates.RPCoarse:
|
|
248
|
-
case dataEntryStates.RPFine:
|
|
249
|
-
switch(channelObject.RPValue)
|
|
250
|
-
{
|
|
251
|
-
default:
|
|
252
|
-
break;
|
|
253
|
-
|
|
254
|
-
// pitch bend range fine tune is not supported in the SoundFont2 format. (pitchbend range is in semitones rather than cents)
|
|
255
|
-
case 0x0000:
|
|
256
|
-
break;
|
|
257
|
-
|
|
258
|
-
// fine tuning
|
|
259
|
-
case 0x0001:
|
|
260
|
-
// grab the data and shift
|
|
261
|
-
const coarse = channelObject.customControllers[customControllers.channelTuning];
|
|
262
|
-
const finalTuning = (coarse << 7) | dataValue;
|
|
263
|
-
this.setChannelTuning(channel, finalTuning * 0.0122); // multiply by 8192 / 100 (cent increment)
|
|
264
|
-
break;
|
|
265
|
-
|
|
266
|
-
// modulation depth
|
|
267
|
-
case 0x0005:
|
|
268
|
-
const currentModulationDepthCents = channelObject.customControllers[customControllers.modulationMultiplier] * 50;
|
|
269
|
-
let cents = currentModulationDepthCents + (dataValue / 128) * 100;
|
|
270
|
-
this.setModulationDepth(channel, cents);
|
|
271
|
-
break
|
|
272
|
-
|
|
273
|
-
case 0x3FFF:
|
|
274
|
-
this.resetParameters(channel);
|
|
275
|
-
break;
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
}
|
|
280
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { generatorTypes } from '../../../soundfont/chunk/generators.js'
|
|
2
|
-
import { consoleColors } from '../../../utils/other.js'
|
|
3
|
-
import { SpessaSynthInfo, SpessaSynthWarn } from '../../../utils/loggin.js'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Releases a note
|
|
7
|
-
* @param channel {number} - The MIDI channel to use
|
|
8
|
-
* @param midiNote {number} - The MIDI key number
|
|
9
|
-
* @this {Synthesizer}
|
|
10
|
-
*/
|
|
11
|
-
export function noteOff(channel, midiNote)
|
|
12
|
-
{
|
|
13
|
-
if(midiNote > 127 || midiNote < 0)
|
|
14
|
-
{
|
|
15
|
-
SpessaSynthWarn(`Received a noteOn for note`, midiNote, "Ignoring.");
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// if high performance mode, kill notes instead of stopping them
|
|
20
|
-
if(this.highPerformanceMode)
|
|
21
|
-
{
|
|
22
|
-
// if the channel is percussion channel, do not kill the notes
|
|
23
|
-
if(!this.workletProcessorChannels[channel].drumChannel)
|
|
24
|
-
{
|
|
25
|
-
this.killNote(channel, midiNote);
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const channelVoices = this.workletProcessorChannels[channel].voices;
|
|
31
|
-
channelVoices.forEach(v => {
|
|
32
|
-
if(v.midiNote !== midiNote || v.isInRelease === true)
|
|
33
|
-
{
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
// if hold pedal, move to sustain
|
|
37
|
-
if(this.workletProcessorChannels[channel].holdPedal) {
|
|
38
|
-
this.workletProcessorChannels[channel].sustainedVoices.push(v);
|
|
39
|
-
}
|
|
40
|
-
else
|
|
41
|
-
{
|
|
42
|
-
this.releaseVoice(v);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Stops a note nearly instantly
|
|
49
|
-
* @param channel {number} - The MIDI channel to use
|
|
50
|
-
* @param midiNote {number} - The MIDI key number
|
|
51
|
-
* @this {Synthesizer}
|
|
52
|
-
*/
|
|
53
|
-
export function killNote(channel, midiNote)
|
|
54
|
-
{
|
|
55
|
-
this.workletProcessorChannels[channel].voices.forEach(v => {
|
|
56
|
-
if(v.midiNote !== midiNote)
|
|
57
|
-
{
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
v.modulatedGenerators[generatorTypes.releaseVolEnv] = -12000; // set release to be very short
|
|
61
|
-
this.releaseVoice(v);
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* stops all notes
|
|
67
|
-
* @param channel {number} - The MIDI channel to use
|
|
68
|
-
* @param force {boolean} - If the notes should stop instantly or release normally
|
|
69
|
-
* @this {Synthesizer}
|
|
70
|
-
*/
|
|
71
|
-
export function stopAll(channel, force = false)
|
|
72
|
-
{
|
|
73
|
-
const channelVoices = this.workletProcessorChannels[channel].voices;
|
|
74
|
-
if(force)
|
|
75
|
-
{
|
|
76
|
-
// force stop all
|
|
77
|
-
channelVoices.length = 0;
|
|
78
|
-
this.workletProcessorChannels[channel].sustainedVoices.length = 0;
|
|
79
|
-
}
|
|
80
|
-
else
|
|
81
|
-
{
|
|
82
|
-
channelVoices.forEach(v => {
|
|
83
|
-
if(v.isInRelease) return;
|
|
84
|
-
this.releaseVoice(v);
|
|
85
|
-
});
|
|
86
|
-
this.workletProcessorChannels[channel].sustainedVoices.forEach(v => {
|
|
87
|
-
this.releaseVoice(v);
|
|
88
|
-
})
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* @this {Synthesizer}
|
|
94
|
-
* @param force {boolean} - If the notes should stop instantly or release normally
|
|
95
|
-
*/
|
|
96
|
-
export function stopAllChannels(force = false)
|
|
97
|
-
{
|
|
98
|
-
SpessaSynthInfo("%cStop all received!", consoleColors.info);
|
|
99
|
-
for (let i = 0; i < this.workletProcessorChannels.length; i++) {
|
|
100
|
-
this.stopAll(i, force);
|
|
101
|
-
}
|
|
102
|
-
}
|