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,77 +0,0 @@
|
|
|
1
|
-
import { getWorkletVoices } from '../worklet_utilities/worklet_voice.js'
|
|
2
|
-
import { generatorTypes } from '../../../soundfont/chunk/generators.js'
|
|
3
|
-
import { computeModulators } from '../worklet_utilities/worklet_modulator.js'
|
|
4
|
-
import { VOICE_CAP } from "../../synthesizer.js";
|
|
5
|
-
import { SpessaSynthWarn } from '../../../utils/loggin.js'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Starts playing a MIDI note
|
|
9
|
-
* @param channel {number} - The MIDI Channel to use
|
|
10
|
-
* @param midiNote {number} - The MIDI key number
|
|
11
|
-
* @param velocity {number} - The velocity (how hard is the key pressed)
|
|
12
|
-
* @param enableDebugging {boolean} - used internally, ignore
|
|
13
|
-
* @this {Synthesizer}
|
|
14
|
-
*/
|
|
15
|
-
export function noteOn(channel, midiNote, velocity, enableDebugging = false)
|
|
16
|
-
{
|
|
17
|
-
if (velocity === 0) {
|
|
18
|
-
this.noteOff(channel, midiNote);
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (
|
|
23
|
-
(this.highPerformanceMode && this.totalVoicesAmount > 200 && velocity < 40) ||
|
|
24
|
-
(this.highPerformanceMode && velocity < 10) ||
|
|
25
|
-
(this.workletProcessorChannels[channel].isMuted)
|
|
26
|
-
) {
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if(midiNote > 127 || midiNote < 0)
|
|
31
|
-
{
|
|
32
|
-
SpessaSynthWarn(`Received a noteOn for note`, midiNote, "Ignoring.");
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
// get voices
|
|
38
|
-
const voices = getWorkletVoices(
|
|
39
|
-
channel,
|
|
40
|
-
midiNote,
|
|
41
|
-
velocity,
|
|
42
|
-
this.workletProcessorChannels[channel].preset,
|
|
43
|
-
this.currentTime,
|
|
44
|
-
this.sampleRate,
|
|
45
|
-
data => this.sampleDump(data.channel, data.sampleID, data.sampleData),
|
|
46
|
-
this.workletProcessorChannels[channel].cachedVoices,
|
|
47
|
-
enableDebugging);
|
|
48
|
-
|
|
49
|
-
// add voices and exclusive class apply
|
|
50
|
-
const channelVoices = this.workletProcessorChannels[channel].voices;
|
|
51
|
-
voices.forEach(voice => {
|
|
52
|
-
const exclusive = voice.generators[generatorTypes.exclusiveClass];
|
|
53
|
-
if(exclusive !== 0)
|
|
54
|
-
{
|
|
55
|
-
channelVoices.forEach(v => {
|
|
56
|
-
if(v.generators[generatorTypes.exclusiveClass] === exclusive)
|
|
57
|
-
{
|
|
58
|
-
this.releaseVoice(v);
|
|
59
|
-
v.generators[generatorTypes.releaseVolEnv] = -7200; // make the release nearly instant
|
|
60
|
-
computeModulators(v, this.workletProcessorChannels[channel].midiControllers);
|
|
61
|
-
}
|
|
62
|
-
})
|
|
63
|
-
}
|
|
64
|
-
computeModulators(voice, this.workletProcessorChannels[channel].midiControllers);
|
|
65
|
-
voice.currentAttenuationDb = 100;
|
|
66
|
-
// set initial pan to avoid split second changing from middle to the correct value
|
|
67
|
-
voice.currentPan = ( (Math.max(-500, Math.min(500, voice.modulatedGenerators[generatorTypes.pan] )) + 500) / 1000) // 0 to 1
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
this.totalVoicesAmount += voices.length;
|
|
71
|
-
// cap the voices
|
|
72
|
-
if(this.totalVoicesAmount > VOICE_CAP)
|
|
73
|
-
{
|
|
74
|
-
this.voiceKilling(voices.length);
|
|
75
|
-
}
|
|
76
|
-
channelVoices.push(...voices);
|
|
77
|
-
}
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import { midiControllers } from '../../../midi_parser/midi_message.js'
|
|
2
|
-
import { SoundFont2 } from '../../../soundfont/soundfont_parser.js'
|
|
3
|
-
import { clearSamplesList } from '../worklet_utilities/worklet_voice.js'
|
|
4
|
-
import { generatorTypes } from '../../../soundfont/chunk/generators.js'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* executes a program change
|
|
8
|
-
* @param channel {number} - The MIDI Channel to use
|
|
9
|
-
* @param programNumber {number} - The MIDI program number
|
|
10
|
-
* @this {Synthesizer}
|
|
11
|
-
*/
|
|
12
|
-
export function programChange(channel, programNumber)
|
|
13
|
-
{
|
|
14
|
-
/**
|
|
15
|
-
* @type {WorkletProcessorChannel}
|
|
16
|
-
*/
|
|
17
|
-
const channelObject = this.workletProcessorChannels[channel];
|
|
18
|
-
if(channelObject.lockPreset)
|
|
19
|
-
{
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
// always 128 for percussion
|
|
23
|
-
const bank = (channelObject.drumChannel ? 128 : channelObject.midiControllers[midiControllers.bankSelect]);
|
|
24
|
-
const preset = this.soundfont.getPreset(bank, programNumber);
|
|
25
|
-
this.setPreset(channel, preset);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* @param channel {number}
|
|
30
|
-
* @param preset {Preset}
|
|
31
|
-
* @this {Synthesizer}
|
|
32
|
-
*/
|
|
33
|
-
export function setPreset(channel, preset)
|
|
34
|
-
{
|
|
35
|
-
if(this.workletProcessorChannels[channel].lockPreset)
|
|
36
|
-
{
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
this.workletProcessorChannels[channel].preset = preset;
|
|
40
|
-
|
|
41
|
-
// reset cached voices
|
|
42
|
-
this.workletProcessorChannels[channel].cachedVoices = [];
|
|
43
|
-
for (let i = 0; i < 128; i++) {
|
|
44
|
-
this.workletProcessorChannels[channel].cachedVoices.push([]);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Toggles drums on a given channel
|
|
50
|
-
* @param channel {number} - The MIDI Channel to use
|
|
51
|
-
* @param isDrum {boolean} - boolean, if the channel should be drums
|
|
52
|
-
* @this {Synthesizer}
|
|
53
|
-
*/
|
|
54
|
-
export function setDrums(channel, isDrum)
|
|
55
|
-
{
|
|
56
|
-
const channelObject = this.workletProcessorChannels[channel];
|
|
57
|
-
if(isDrum)
|
|
58
|
-
{
|
|
59
|
-
channelObject.drumChannel = true;
|
|
60
|
-
this.setPreset(channel, this.soundfont.getPreset(128, channelObject.preset.program));
|
|
61
|
-
}
|
|
62
|
-
else
|
|
63
|
-
{
|
|
64
|
-
channelObject.percussionChannel = false;
|
|
65
|
-
this.setPreset(channel, this.soundfont.getPreset(0, channelObject.preset.program));
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Reloads the soundfont, stops all voices
|
|
71
|
-
* @param buffer {ArrayBuffer} - the new soundfont buffer
|
|
72
|
-
* @this {Synthesizer}
|
|
73
|
-
*/
|
|
74
|
-
export function reloadSoundFont(buffer)
|
|
75
|
-
{
|
|
76
|
-
this.stopAllChannels(true);
|
|
77
|
-
delete this.soundfont;
|
|
78
|
-
clearSamplesList();
|
|
79
|
-
delete this.workletDumpedSamplesList;
|
|
80
|
-
this.workletDumpedSamplesList = [];
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
this.soundfont = new SoundFont2(buffer);
|
|
84
|
-
this.defaultPreset = this.soundfont.getPreset(0, 0);
|
|
85
|
-
this.drumPreset = this.soundfont.getPreset(128, 0);
|
|
86
|
-
|
|
87
|
-
for(let i = 0; i < this.workletProcessorChannels.length; i++)
|
|
88
|
-
{
|
|
89
|
-
const channelObject = this.workletProcessorChannels[i];
|
|
90
|
-
channelObject.cachedVoices = [];
|
|
91
|
-
for (let j = 0; j < 128; j++) {
|
|
92
|
-
channelObject.cachedVoices.push([]);
|
|
93
|
-
}
|
|
94
|
-
channelObject.lockPreset = false;
|
|
95
|
-
this.programChange(i, channelObject.preset.program);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* saves a sample
|
|
101
|
-
* @param channel {number}
|
|
102
|
-
* @param sampleID {number}
|
|
103
|
-
* @param sampleData {Float32Array}
|
|
104
|
-
* @this {Synthesizer}
|
|
105
|
-
*/
|
|
106
|
-
export function sampleDump(channel, sampleID, sampleData)
|
|
107
|
-
{
|
|
108
|
-
this.workletDumpedSamplesList[sampleID] = sampleData;
|
|
109
|
-
// the sample maybe was loaded after the voice was sent... adjust the end position!
|
|
110
|
-
|
|
111
|
-
// not for all channels because the system tells us for what channel this voice was dumped! yay!
|
|
112
|
-
this.workletProcessorChannels[channel].voices.forEach(v => {
|
|
113
|
-
if(v.sample.sampleID !== sampleID)
|
|
114
|
-
{
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
v.sample.end = sampleData.length - 1 + v.generators[generatorTypes.endAddrOffset] + (v.generators[generatorTypes.endAddrsCoarseOffset] * 32768);
|
|
118
|
-
// calculate for how long the sample has been playing and move the cursor there
|
|
119
|
-
v.sample.cursor = (v.sample.playbackStep * this.sampleRate) * (this.currentTime - v.startTime);
|
|
120
|
-
if(v.sample.loopingMode === 0) // no loop
|
|
121
|
-
{
|
|
122
|
-
if (v.sample.cursor >= v.sample.end)
|
|
123
|
-
{
|
|
124
|
-
v.finished = true;
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
else
|
|
129
|
-
{
|
|
130
|
-
// go through modulo (adjust cursor if the sample has looped
|
|
131
|
-
if(v.sample.cursor > v.sample.loopEnd)
|
|
132
|
-
{
|
|
133
|
-
v.sample.cursor = v.sample.cursor % (v.sample.loopEnd - v.sample.loopStart) + v.sample.loopStart - 1;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
// set start time to current!
|
|
137
|
-
v.startTime = this.currentTime;
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
}
|
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
import { arrayToHexString, consoleColors } from '../../../utils/other.js'
|
|
2
|
-
import { SpessaSynthInfo, SpessaSynthWarn } from '../../../utils/loggin.js'
|
|
3
|
-
/**
|
|
4
|
-
* Executes a system exclusive
|
|
5
|
-
* @param messageData {number[]} - The binary message data without f0
|
|
6
|
-
* @param channelOffset {number} - Channel offset (for midi ports). Defaults to 0.
|
|
7
|
-
* @this {Synthesizer}
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
export function systemExclusive(messageData, channelOffset = 0)
|
|
11
|
-
{
|
|
12
|
-
const type = messageData[0];
|
|
13
|
-
switch (type)
|
|
14
|
-
{
|
|
15
|
-
default:
|
|
16
|
-
SpessaSynthWarn(`%cUnrecognized SysEx: %c${arrayToHexString(messageData)}`,
|
|
17
|
-
consoleColors.warn,
|
|
18
|
-
consoleColors.unrecognized);
|
|
19
|
-
break;
|
|
20
|
-
|
|
21
|
-
// non realtime
|
|
22
|
-
case 0x7E:
|
|
23
|
-
// gm system
|
|
24
|
-
if(messageData[2] === 0x09)
|
|
25
|
-
{
|
|
26
|
-
if(messageData[3] === 0x01)
|
|
27
|
-
{
|
|
28
|
-
SpessaSynthInfo("%cGM system on", consoleColors.info);
|
|
29
|
-
this.system = "gm";
|
|
30
|
-
}
|
|
31
|
-
else if(messageData[3] === 0x03)
|
|
32
|
-
{
|
|
33
|
-
SpessaSynthInfo("%cGM2 system on", consoleColors.info);
|
|
34
|
-
this.system = "gm2";
|
|
35
|
-
}
|
|
36
|
-
else
|
|
37
|
-
{
|
|
38
|
-
SpessaSynthInfo("%cGM system off, defaulting to GS", consoleColors.info);
|
|
39
|
-
this.system = "gs";
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
break;
|
|
43
|
-
|
|
44
|
-
// realtime
|
|
45
|
-
case 0x7F:
|
|
46
|
-
if(messageData[2] === 0x04 && messageData[3] === 0x01)
|
|
47
|
-
{
|
|
48
|
-
// main volume
|
|
49
|
-
const vol = messageData[5] << 7 | messageData[4];
|
|
50
|
-
this.setMainVolume(vol / 16384);
|
|
51
|
-
SpessaSynthInfo(`%cMaster Volume. Volume: %c${vol}`,
|
|
52
|
-
consoleColors.info,
|
|
53
|
-
consoleColors.value);
|
|
54
|
-
}
|
|
55
|
-
else
|
|
56
|
-
if(messageData[2] === 0x04 && messageData[3] === 0x03)
|
|
57
|
-
{
|
|
58
|
-
// fine tuning
|
|
59
|
-
const tuningValue = ((messageData[5] << 7) | messageData[6]) - 8192;
|
|
60
|
-
const cents = Math.floor(tuningValue / 81.92); // [-100;+99] cents range
|
|
61
|
-
this.setMasterTuning(cents);
|
|
62
|
-
SpessaSynthInfo(`%cMaster Fine Tuning. Cents: %c${cents}`,
|
|
63
|
-
consoleColors.info,
|
|
64
|
-
consoleColors.value)
|
|
65
|
-
}
|
|
66
|
-
else
|
|
67
|
-
if(messageData[2] === 0x04 && messageData[3] === 0x04)
|
|
68
|
-
{
|
|
69
|
-
// coarse tuning
|
|
70
|
-
// lsb is ignored
|
|
71
|
-
const semitones = messageData[5] - 64;
|
|
72
|
-
const cents = semitones * 100;
|
|
73
|
-
this.setMasterTuning(cents);
|
|
74
|
-
SpessaSynthInfo(`%cMaster Coarse Tuning. Cents: %c${cents}`,
|
|
75
|
-
consoleColors.info,
|
|
76
|
-
consoleColors.value)
|
|
77
|
-
}
|
|
78
|
-
else
|
|
79
|
-
{
|
|
80
|
-
SpessaSynthWarn(
|
|
81
|
-
`%cUnrecognized MIDI Real-time message: %c${arrayToHexString(messageData)}`,
|
|
82
|
-
consoleColors.warn,
|
|
83
|
-
consoleColors.unrecognized)
|
|
84
|
-
}
|
|
85
|
-
break;
|
|
86
|
-
|
|
87
|
-
// this is a roland sysex
|
|
88
|
-
// http://www.bandtrax.com.au/sysex.htm
|
|
89
|
-
// https://cdn.roland.com/assets/media/pdf/AT-20R_30R_MI.pdf
|
|
90
|
-
case 0x41:
|
|
91
|
-
// messagedata[1] is device id (ignore as we're everything >:) )
|
|
92
|
-
if(messageData[2] === 0x42 && messageData[3] === 0x12)
|
|
93
|
-
{
|
|
94
|
-
// this is a GS sysex
|
|
95
|
-
// messageData[5] and [6] is the system parameter, messageData[7] is the value
|
|
96
|
-
const messageValue = messageData[7];
|
|
97
|
-
if(messageData[6] === 0x7F)
|
|
98
|
-
{
|
|
99
|
-
// GS mode set
|
|
100
|
-
if(messageValue === 0x00) {
|
|
101
|
-
// this is a GS reset
|
|
102
|
-
SpessaSynthInfo("%cGS system on", consoleColors.info);
|
|
103
|
-
this.system = "gs";
|
|
104
|
-
}
|
|
105
|
-
else if(messageValue === 0x7F)
|
|
106
|
-
{
|
|
107
|
-
// GS mode off
|
|
108
|
-
SpessaSynthInfo("%cGS system off, switching to GM2", consoleColors.info);
|
|
109
|
-
this.system = "gm2";
|
|
110
|
-
}
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
else
|
|
114
|
-
if(messageData[4] === 0x40)
|
|
115
|
-
{
|
|
116
|
-
// this is a system parameter
|
|
117
|
-
if((messageData[5] & 0x10) > 0)
|
|
118
|
-
{
|
|
119
|
-
// this is an individual part (channel) parameter
|
|
120
|
-
// determine the channel 0 means channel 10 (default), 1 means 1 etc.
|
|
121
|
-
const channel = [9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15][messageData[5] & 0x0F] + channelOffset; // for example 1A means A = 11, which corresponds to channel 12 (counting from 1)
|
|
122
|
-
switch (messageData[6])
|
|
123
|
-
{
|
|
124
|
-
default:
|
|
125
|
-
break;
|
|
126
|
-
|
|
127
|
-
case 0x15:
|
|
128
|
-
// this is the Use for Drum Part sysex (multiple drums)
|
|
129
|
-
this.setDrums(channel, messageValue > 0 && messageData[5] >> 4); // if set to other than 0, is a drum channel
|
|
130
|
-
SpessaSynthInfo(
|
|
131
|
-
`%cChannel %c${channel}%c ${messageValue > 0 && messageData[5] >> 4 ?
|
|
132
|
-
"is now a drum channel"
|
|
133
|
-
:
|
|
134
|
-
"now isn't a drum channel"
|
|
135
|
-
}%c via: %c${arrayToHexString(messageData)}`,
|
|
136
|
-
consoleColors.info,
|
|
137
|
-
consoleColors.value,
|
|
138
|
-
consoleColors.recognized,
|
|
139
|
-
consoleColors.info,
|
|
140
|
-
consoleColors.value);
|
|
141
|
-
return;
|
|
142
|
-
|
|
143
|
-
case 0x16:
|
|
144
|
-
// this is the pitch key shift sysex
|
|
145
|
-
const keyShift = messageValue - 64;
|
|
146
|
-
this.transposeChannel(channel, keyShift);
|
|
147
|
-
SpessaSynthInfo(`%cChannel %c${channel}%c pitch shift. Semitones %c${keyShift}%c, with %c${arrayToHexString(messageData)}`,
|
|
148
|
-
consoleColors.info,
|
|
149
|
-
consoleColors.recognized,
|
|
150
|
-
consoleColors.info,
|
|
151
|
-
consoleColors.value,
|
|
152
|
-
consoleColors.info,
|
|
153
|
-
consoleColors.value);
|
|
154
|
-
return;
|
|
155
|
-
|
|
156
|
-
case 0x40:
|
|
157
|
-
case 0x41:
|
|
158
|
-
case 0x42:
|
|
159
|
-
case 0x43:
|
|
160
|
-
case 0x44:
|
|
161
|
-
case 0x45:
|
|
162
|
-
case 0x46:
|
|
163
|
-
case 0x47:
|
|
164
|
-
case 0x48:
|
|
165
|
-
case 0x49:
|
|
166
|
-
case 0x4A:
|
|
167
|
-
case 0x4B:
|
|
168
|
-
// scale tuning
|
|
169
|
-
const cents = messageValue - 64;
|
|
170
|
-
SpessaSynthInfo(`%cChannel %c${channel}%c tuning. Cents %c${cents}%c, with %c${arrayToHexString(messageData)}`,
|
|
171
|
-
consoleColors.info,
|
|
172
|
-
consoleColors.recognized,
|
|
173
|
-
consoleColors.info,
|
|
174
|
-
consoleColors.value,
|
|
175
|
-
consoleColors.info,
|
|
176
|
-
consoleColors.value);
|
|
177
|
-
this.setChannelTuning(channel, cents);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
else
|
|
181
|
-
// this is a global system parameter
|
|
182
|
-
if(messageData[5] === 0x00 && messageData[6] === 0x06)
|
|
183
|
-
{
|
|
184
|
-
// roland master pan
|
|
185
|
-
SpessaSynthInfo(`%cRoland GS Master Pan set to: %c${messageValue}%c with: %c${arrayToHexString(messageData)}`,
|
|
186
|
-
consoleColors.info,
|
|
187
|
-
consoleColors.value,
|
|
188
|
-
consoleColors.info,
|
|
189
|
-
consoleColors.value);
|
|
190
|
-
this.setMasterPan((messageValue - 64) / 64);
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
else
|
|
194
|
-
if(messageData[5] === 0x00 && messageData[6] === 0x05)
|
|
195
|
-
{
|
|
196
|
-
// roland master key shift (transpose)
|
|
197
|
-
const transpose = messageValue - 64;
|
|
198
|
-
SpessaSynthInfo(`%cRoland GS Master Key-Shift set to: %c${transpose}%c with: %c${arrayToHexString(messageData)}`,
|
|
199
|
-
consoleColors.info,
|
|
200
|
-
consoleColors.value,
|
|
201
|
-
consoleColors.info,
|
|
202
|
-
consoleColors.value);
|
|
203
|
-
this.setMasterTuning(transpose * 100);
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
else
|
|
207
|
-
if(messageData[5] === 0x00 && messageData[6] === 0x04)
|
|
208
|
-
{
|
|
209
|
-
// roland GS master volume
|
|
210
|
-
SpessaSynthInfo(`%cRoland GS Master Volume set to: %c${messageValue}%c with: %c${arrayToHexString(messageData)}`,
|
|
211
|
-
consoleColors.info,
|
|
212
|
-
consoleColors.value,
|
|
213
|
-
consoleColors.info,
|
|
214
|
-
consoleColors.value);
|
|
215
|
-
this.setMainVolume(messageValue / 127);
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
// this is some other GS sysex...
|
|
220
|
-
SpessaSynthWarn(`%cUnrecognized Roland %cGS %cSysEx: %c${arrayToHexString(messageData)}`,
|
|
221
|
-
consoleColors.warn,
|
|
222
|
-
consoleColors.recognized,
|
|
223
|
-
consoleColors.warn,
|
|
224
|
-
consoleColors.unrecognized);
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
else
|
|
228
|
-
if(messageData[2] === 0x16 && messageData[3] === 0x12 && messageData[4] === 0x10)
|
|
229
|
-
{
|
|
230
|
-
// this is a roland master volume message
|
|
231
|
-
this.setMainVolume(messageData[7] / 100);
|
|
232
|
-
SpessaSynthInfo(`%cRoland Master Volume control set to: %c${messageData[7]}%c via: %c${arrayToHexString(messageData)}`,
|
|
233
|
-
consoleColors.info,
|
|
234
|
-
consoleColors.value,
|
|
235
|
-
consoleColors.info,
|
|
236
|
-
consoleColors.value);
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
else
|
|
240
|
-
{
|
|
241
|
-
// this is something else...
|
|
242
|
-
SpessaSynthWarn(`%cUnrecognized Roland SysEx: %c${arrayToHexString(messageData)}`,
|
|
243
|
-
consoleColors.warn,
|
|
244
|
-
consoleColors.unrecognized);
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// yamaha
|
|
249
|
-
case 0x43:
|
|
250
|
-
// XG on
|
|
251
|
-
if(messageData[2] === 0x4C && messageData[5] === 0x7E && messageData[6] === 0x00)
|
|
252
|
-
{
|
|
253
|
-
SpessaSynthInfo("%cXG system on", consoleColors.info);
|
|
254
|
-
this.system = "xg";
|
|
255
|
-
}
|
|
256
|
-
else
|
|
257
|
-
{
|
|
258
|
-
SpessaSynthWarn(`%cUnrecognized Yamaha SysEx: %c${arrayToHexString(messageData)}`,
|
|
259
|
-
consoleColors.warn,
|
|
260
|
-
consoleColors.unrecognized);
|
|
261
|
-
}
|
|
262
|
-
break;
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
}
|
|
266
|
-
}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import {customControllers, NON_CC_INDEX_OFFSET} from '../worklet_utilities/worklet_processor_channel.js'
|
|
2
|
-
import {consoleColors} from '../../../utils/other.js'
|
|
3
|
-
import {modulatorSources} from '../../../soundfont/chunk/modulators.js'
|
|
4
|
-
import {computeModulators} from '../worklet_utilities/worklet_modulator.js'
|
|
5
|
-
import {SpessaSynthInfo} from '../../../utils/loggin.js'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Transposes all channels by given amount of semitones
|
|
9
|
-
* @this {Synthesizer}
|
|
10
|
-
* @param semitones {number} Can be float
|
|
11
|
-
* @param force {boolean} defaults to false, if true transposes the channel even if it's a drum channel
|
|
12
|
-
*/
|
|
13
|
-
export function transposeAllChannels(semitones, force = false)
|
|
14
|
-
{
|
|
15
|
-
this.transposition = 0;
|
|
16
|
-
for (let i = 0; i < this.workletProcessorChannels.length; i++) {
|
|
17
|
-
this.transposeChannel(i, semitones, force);
|
|
18
|
-
}
|
|
19
|
-
this.transposition = semitones;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Transposes the channel by given amount of semitones
|
|
24
|
-
* @this {Synthesizer}
|
|
25
|
-
* @param channel {number} - The MIDI Channel to use
|
|
26
|
-
* @param semitones {number} - Can be a float
|
|
27
|
-
* @param force {boolean} - defaults to false, if true transposes the channel even if it's a drum channel
|
|
28
|
-
*/
|
|
29
|
-
export function transposeChannel(channel, semitones, force=false)
|
|
30
|
-
{
|
|
31
|
-
semitones += this.transposition;
|
|
32
|
-
const channelObject = this.workletProcessorChannels[channel];
|
|
33
|
-
if(channelObject.drumChannel && !force)
|
|
34
|
-
{
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
channelObject.customControllers[customControllers.channelTranspose] = semitones * 100;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Sets the channel's tuning
|
|
42
|
-
* @this {Synthesizer}
|
|
43
|
-
* @param channel {number}
|
|
44
|
-
* @param cents {number}
|
|
45
|
-
*/
|
|
46
|
-
export function setChannelTuning(channel, cents)
|
|
47
|
-
{
|
|
48
|
-
const channelObject = this.workletProcessorChannels[channel];
|
|
49
|
-
cents = Math.round(cents);
|
|
50
|
-
channelObject.customControllers[customControllers.channelTuning] = cents;
|
|
51
|
-
SpessaSynthInfo(`%cChannel ${channel} tuning. Cents: %c${cents}`,
|
|
52
|
-
consoleColors.info,
|
|
53
|
-
consoleColors.value);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Sets the worklet's master tuning
|
|
58
|
-
* @this {Synthesizer}
|
|
59
|
-
* @param cents {number}
|
|
60
|
-
*/
|
|
61
|
-
export function setMasterTuning(cents)
|
|
62
|
-
{
|
|
63
|
-
cents = Math.round(cents);
|
|
64
|
-
for (let i = 0; i < this.workletProcessorChannels.length; i++) {
|
|
65
|
-
this.workletProcessorChannels[i].customControllers[customControllers.masterTuning] = cents;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* @this {Synthesizer}
|
|
71
|
-
* @param channel {number}
|
|
72
|
-
* @param cents {number}
|
|
73
|
-
*/
|
|
74
|
-
export function setModulationDepth(channel, cents)
|
|
75
|
-
{
|
|
76
|
-
let channelObject = this.workletProcessorChannels[channel];
|
|
77
|
-
cents = Math.round(cents);
|
|
78
|
-
SpessaSynthInfo(`%cChannel ${channel} modulation depth. Cents: %c${cents}`,
|
|
79
|
-
consoleColors.info,
|
|
80
|
-
consoleColors.value);
|
|
81
|
-
/* ==============
|
|
82
|
-
IMPORTANT
|
|
83
|
-
here we convert cents into a multiplier.
|
|
84
|
-
midi spec assumes the default is 50 cents,
|
|
85
|
-
but it might be different for the soundfont,
|
|
86
|
-
so we create a multiplier by divinging cents by 50.
|
|
87
|
-
for example, if we want 100 cents, then multiplier will be 2,
|
|
88
|
-
which for a preset with depth of 50 will create 100.
|
|
89
|
-
================*/
|
|
90
|
-
channelObject.customControllers[customControllers.modulationMultiplier] = cents / 50;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Sets the pitch of the given channel
|
|
95
|
-
* @this {Synthesizer}
|
|
96
|
-
* @param channel {number} usually 0-15: the channel to change pitch
|
|
97
|
-
* @param MSB {number} SECOND byte of the MIDI pitchWheel message
|
|
98
|
-
* @param LSB {number} FIRST byte of the MIDI pitchWheel message
|
|
99
|
-
*/
|
|
100
|
-
export function pitchWheel(channel, MSB, LSB)
|
|
101
|
-
{
|
|
102
|
-
this.workletProcessorChannels[channel].midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheel] = (LSB | (MSB << 7));
|
|
103
|
-
this.workletProcessorChannels[channel].voices.forEach(v => computeModulators(v, this.workletProcessorChannels[channel].midiControllers));
|
|
104
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @param channel {number}
|
|
3
|
-
* @this {Synthesizer}
|
|
4
|
-
*/
|
|
5
|
-
export function disableAndLockVibrato(channel)
|
|
6
|
-
{
|
|
7
|
-
this.workletProcessorChannels[channel].lockVibrato = true;
|
|
8
|
-
this.workletProcessorChannels[channel].channelVibrato.rate = 0;
|
|
9
|
-
this.workletProcessorChannels[channel].channelVibrato.delay = 0;
|
|
10
|
-
this.workletProcessorChannels[channel].channelVibrato.depth = 0;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* @param channel {number}
|
|
15
|
-
* @param depth {number}
|
|
16
|
-
* @param rate {number}
|
|
17
|
-
* @param delay {number}
|
|
18
|
-
* @this {Synthesizer}
|
|
19
|
-
*/
|
|
20
|
-
export function setVibrato(channel, depth, rate, delay)
|
|
21
|
-
{
|
|
22
|
-
if(this.workletProcessorChannels[channel].lockVibrato)
|
|
23
|
-
{
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
this.workletProcessorChannels[channel].vibrato.rate = rate;
|
|
27
|
-
this.workletProcessorChannels[channel].vibrato.delay = delay;
|
|
28
|
-
this.workletProcessorChannels[channel].vibrato.depth = depth;
|
|
29
|
-
}
|