spessasynth_lib 3.25.22 → 3.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -114
- package/index.js +33 -33
- package/package.json +16 -6
- package/external_midi/README.md +0 -4
- package/external_midi/midi_handler.js +0 -130
- package/external_midi/web_midi_link.js +0 -43
- package/externals/fflate/LICENSE +0 -21
- package/externals/fflate/fflate.min.js +0 -1
- package/externals/stbvorbis_sync/@types/stbvorbis_sync.d.ts +0 -12
- package/externals/stbvorbis_sync/LICENSE +0 -202
- package/externals/stbvorbis_sync/NOTICE +0 -6
- package/externals/stbvorbis_sync/stbvorbis_sync.min.js +0 -1
- package/midi/README.md +0 -32
- package/midi/basic_midi.js +0 -565
- package/midi/midi_builder.js +0 -202
- package/midi/midi_data.js +0 -63
- package/midi/midi_loader.js +0 -324
- package/midi/midi_message.js +0 -254
- package/midi/midi_sequence.js +0 -225
- package/midi/midi_tools/get_note_times.js +0 -154
- package/midi/midi_tools/midi_editor.js +0 -611
- package/midi/midi_tools/midi_writer.js +0 -99
- package/midi/midi_tools/rmidi_writer.js +0 -567
- package/midi/midi_tools/used_keys_loaded.js +0 -238
- package/midi/xmf_loader.js +0 -454
- package/sequencer/README.md +0 -32
- package/sequencer/sequencer_engine/events.js +0 -207
- package/sequencer/sequencer_engine/play.js +0 -353
- package/sequencer/sequencer_engine/process_event.js +0 -169
- package/sequencer/sequencer_engine/process_tick.js +0 -106
- package/sequencer/sequencer_engine/sequencer_engine.js +0 -337
- package/sequencer/sequencer_engine/song_control.js +0 -229
- package/sequencer/worklet_wrapper/default_sequencer_options.js +0 -8
- package/sequencer/worklet_wrapper/sequencer.js +0 -807
- package/sequencer/worklet_wrapper/sequencer_message.js +0 -53
- package/soundfont/README.md +0 -13
- package/soundfont/basic_soundfont/basic_instrument.js +0 -77
- package/soundfont/basic_soundfont/basic_preset.js +0 -336
- package/soundfont/basic_soundfont/basic_sample.js +0 -197
- package/soundfont/basic_soundfont/basic_soundfont.js +0 -565
- package/soundfont/basic_soundfont/basic_zone.js +0 -64
- package/soundfont/basic_soundfont/basic_zones.js +0 -43
- package/soundfont/basic_soundfont/generator.js +0 -220
- package/soundfont/basic_soundfont/modulator.js +0 -378
- package/soundfont/basic_soundfont/riff_chunk.js +0 -149
- package/soundfont/basic_soundfont/write_dls/art2.js +0 -173
- package/soundfont/basic_soundfont/write_dls/articulator.js +0 -49
- package/soundfont/basic_soundfont/write_dls/combine_zones.js +0 -400
- package/soundfont/basic_soundfont/write_dls/ins.js +0 -103
- package/soundfont/basic_soundfont/write_dls/lins.js +0 -18
- package/soundfont/basic_soundfont/write_dls/modulator_converter.js +0 -330
- package/soundfont/basic_soundfont/write_dls/rgn2.js +0 -121
- package/soundfont/basic_soundfont/write_dls/wave.js +0 -94
- package/soundfont/basic_soundfont/write_dls/write_dls.js +0 -119
- package/soundfont/basic_soundfont/write_dls/wsmp.js +0 -78
- package/soundfont/basic_soundfont/write_dls/wvpl.js +0 -32
- package/soundfont/basic_soundfont/write_sf2/ibag.js +0 -39
- package/soundfont/basic_soundfont/write_sf2/igen.js +0 -80
- package/soundfont/basic_soundfont/write_sf2/imod.js +0 -46
- package/soundfont/basic_soundfont/write_sf2/inst.js +0 -34
- package/soundfont/basic_soundfont/write_sf2/pbag.js +0 -39
- package/soundfont/basic_soundfont/write_sf2/pgen.js +0 -82
- package/soundfont/basic_soundfont/write_sf2/phdr.js +0 -42
- package/soundfont/basic_soundfont/write_sf2/pmod.js +0 -46
- package/soundfont/basic_soundfont/write_sf2/sdta.js +0 -80
- package/soundfont/basic_soundfont/write_sf2/shdr.js +0 -55
- package/soundfont/basic_soundfont/write_sf2/write.js +0 -222
- package/soundfont/dls/articulator_converter.js +0 -396
- package/soundfont/dls/dls_destinations.js +0 -38
- package/soundfont/dls/dls_preset.js +0 -44
- package/soundfont/dls/dls_sample.js +0 -75
- package/soundfont/dls/dls_soundfont.js +0 -186
- package/soundfont/dls/dls_sources.js +0 -62
- package/soundfont/dls/dls_zone.js +0 -95
- package/soundfont/dls/read_articulation.js +0 -299
- package/soundfont/dls/read_instrument.js +0 -121
- package/soundfont/dls/read_instrument_list.js +0 -17
- package/soundfont/dls/read_lart.js +0 -35
- package/soundfont/dls/read_region.js +0 -152
- package/soundfont/dls/read_samples.js +0 -270
- package/soundfont/load_soundfont.js +0 -21
- package/soundfont/read_sf2/generators.js +0 -46
- package/soundfont/read_sf2/instruments.js +0 -66
- package/soundfont/read_sf2/modulators.js +0 -36
- package/soundfont/read_sf2/presets.js +0 -80
- package/soundfont/read_sf2/samples.js +0 -304
- package/soundfont/read_sf2/soundfont.js +0 -305
- package/soundfont/read_sf2/zones.js +0 -263
- package/synthetizer/README.md +0 -10
- package/synthetizer/audio_effects/effects_config.js +0 -25
- package/synthetizer/audio_effects/fancy_chorus.js +0 -162
- package/synthetizer/audio_effects/rb_compressed.min.js +0 -1
- package/synthetizer/audio_effects/reverb.js +0 -35
- package/synthetizer/audio_effects/reverb_as_binary.js +0 -18
- package/synthetizer/audio_engine/README.md +0 -25
- package/synthetizer/audio_engine/engine_components/compute_modulator.js +0 -266
- package/synthetizer/audio_engine/engine_components/controller_tables.js +0 -88
- package/synthetizer/audio_engine/engine_components/key_modifier_manager.js +0 -149
- package/synthetizer/audio_engine/engine_components/lfo.js +0 -26
- package/synthetizer/audio_engine/engine_components/lowpass_filter.js +0 -282
- package/synthetizer/audio_engine/engine_components/midi_audio_channel.js +0 -471
- package/synthetizer/audio_engine/engine_components/modulation_envelope.js +0 -181
- package/synthetizer/audio_engine/engine_components/modulator_curves.js +0 -89
- package/synthetizer/audio_engine/engine_components/soundfont_manager/sfman_message.js +0 -9
- package/synthetizer/audio_engine/engine_components/soundfont_manager/soundfont_manager.js +0 -254
- package/synthetizer/audio_engine/engine_components/stereo_panner.js +0 -120
- package/synthetizer/audio_engine/engine_components/unit_converter.js +0 -73
- package/synthetizer/audio_engine/engine_components/voice.js +0 -519
- package/synthetizer/audio_engine/engine_components/volume_envelope.js +0 -401
- package/synthetizer/audio_engine/engine_components/wavetable_oscillator.js +0 -263
- package/synthetizer/audio_engine/engine_methods/controller_control/controller_change.js +0 -132
- package/synthetizer/audio_engine/engine_methods/controller_control/master_parameters.js +0 -48
- package/synthetizer/audio_engine/engine_methods/controller_control/reset_controllers.js +0 -241
- package/synthetizer/audio_engine/engine_methods/create_midi_channel.js +0 -27
- package/synthetizer/audio_engine/engine_methods/data_entry/data_entry_coarse.js +0 -253
- package/synthetizer/audio_engine/engine_methods/data_entry/data_entry_fine.js +0 -66
- package/synthetizer/audio_engine/engine_methods/mute_channel.js +0 -17
- package/synthetizer/audio_engine/engine_methods/note_on.js +0 -174
- package/synthetizer/audio_engine/engine_methods/portamento_time.js +0 -92
- package/synthetizer/audio_engine/engine_methods/program_change.js +0 -61
- package/synthetizer/audio_engine/engine_methods/render_voice.js +0 -196
- package/synthetizer/audio_engine/engine_methods/soundfont_management/clear_sound_font.js +0 -30
- package/synthetizer/audio_engine/engine_methods/soundfont_management/get_preset.js +0 -22
- package/synthetizer/audio_engine/engine_methods/soundfont_management/reload_sound_font.js +0 -40
- package/synthetizer/audio_engine/engine_methods/soundfont_management/send_preset_list.js +0 -34
- package/synthetizer/audio_engine/engine_methods/soundfont_management/set_embedded_sound_font.js +0 -21
- package/synthetizer/audio_engine/engine_methods/stopping_notes/kill_note.js +0 -20
- package/synthetizer/audio_engine/engine_methods/stopping_notes/note_off.js +0 -55
- package/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_channels.js +0 -16
- package/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_notes.js +0 -30
- package/synthetizer/audio_engine/engine_methods/stopping_notes/voice_killing.js +0 -63
- package/synthetizer/audio_engine/engine_methods/system_exclusive.js +0 -744
- package/synthetizer/audio_engine/engine_methods/tuning_control/channel_pressure.js +0 -24
- package/synthetizer/audio_engine/engine_methods/tuning_control/pitch_wheel.js +0 -33
- package/synthetizer/audio_engine/engine_methods/tuning_control/poly_pressure.js +0 -31
- package/synthetizer/audio_engine/engine_methods/tuning_control/set_master_tuning.js +0 -15
- package/synthetizer/audio_engine/engine_methods/tuning_control/set_modulation_depth.js +0 -27
- package/synthetizer/audio_engine/engine_methods/tuning_control/set_octave_tuning.js +0 -19
- package/synthetizer/audio_engine/engine_methods/tuning_control/set_tuning.js +0 -27
- package/synthetizer/audio_engine/engine_methods/tuning_control/transpose_all_channels.js +0 -15
- package/synthetizer/audio_engine/engine_methods/tuning_control/transpose_channel.js +0 -34
- package/synthetizer/audio_engine/main_processor.js +0 -765
- package/synthetizer/audio_engine/message_protocol/README.md +0 -13
- package/synthetizer/audio_engine/message_protocol/message_sending.js +0 -22
- package/synthetizer/audio_engine/message_protocol/worklet_message.js +0 -107
- package/synthetizer/audio_engine/snapshot/apply_synthesizer_snapshot.js +0 -14
- package/synthetizer/audio_engine/snapshot/channel_snapshot.js +0 -175
- package/synthetizer/audio_engine/snapshot/synthesizer_snapshot.js +0 -122
- package/synthetizer/synth_constants.js +0 -20
- package/synthetizer/worklet_processor.min.js +0 -21
- package/synthetizer/worklet_wrapper/key_modifier_manager.js +0 -104
- package/synthetizer/worklet_wrapper/synth_event_handler.js +0 -214
- package/synthetizer/worklet_wrapper/synth_soundfont_manager.js +0 -111
- package/synthetizer/worklet_wrapper/synthetizer.js +0 -1027
- package/synthetizer/worklet_wrapper/worklet_processor.js +0 -340
- package/synthetizer/worklet_wrapper/worklet_url.js +0 -16
- package/utils/README.md +0 -5
- package/utils/buffer_to_wav.js +0 -186
- package/utils/byte_functions/big_endian.js +0 -32
- package/utils/byte_functions/little_endian.js +0 -77
- package/utils/byte_functions/string.js +0 -107
- package/utils/byte_functions/variable_length_quantity.js +0 -42
- package/utils/fill_with_defaults.js +0 -21
- package/utils/indexed_array.js +0 -52
- package/utils/loggin.js +0 -79
- package/utils/other.js +0 -92
- package/utils/sysex_detector.js +0 -58
- package/utils/xg_hacks.js +0 -193
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { reverbBufferBinary } from "./reverb_as_binary.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Creates a reverb processor
|
|
5
|
-
* @param context {BaseAudioContext}
|
|
6
|
-
* @param reverbBuffer {AudioBuffer}
|
|
7
|
-
* @returns {{conv: ConvolverNode, promise: Promise<AudioBuffer>}}
|
|
8
|
-
*/
|
|
9
|
-
export function getReverbProcessor(context, reverbBuffer = undefined)
|
|
10
|
-
{
|
|
11
|
-
let solve;
|
|
12
|
-
/**
|
|
13
|
-
* @type {Promise<AudioBuffer>}
|
|
14
|
-
*/
|
|
15
|
-
let promise = new Promise(r => solve = r);
|
|
16
|
-
const convolver = context.createConvolver();
|
|
17
|
-
if (reverbBuffer)
|
|
18
|
-
{
|
|
19
|
-
convolver.buffer = reverbBuffer;
|
|
20
|
-
solve();
|
|
21
|
-
}
|
|
22
|
-
else
|
|
23
|
-
{
|
|
24
|
-
// decode
|
|
25
|
-
promise = context.decodeAudioData(reverbBufferBinary.slice(0));
|
|
26
|
-
promise.then(b =>
|
|
27
|
-
{
|
|
28
|
-
convolver.buffer = b;
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
return {
|
|
32
|
-
conv: convolver,
|
|
33
|
-
promise: promise
|
|
34
|
-
};
|
|
35
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { rbCompressed } from "./rb_compressed.min.js";
|
|
2
|
-
import { inflateSync } from "../../externals/fflate/fflate.min.js";
|
|
3
|
-
|
|
4
|
-
// convert the base64 string to array buffer
|
|
5
|
-
const binaryString = atob(rbCompressed);
|
|
6
|
-
const binary = new Uint8Array(binaryString.length);
|
|
7
|
-
for (let i = 0; i < binaryString.length; i++)
|
|
8
|
-
{
|
|
9
|
-
binary[i] = binaryString.charCodeAt(i);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* the reverb is zlib compressed, decompress here
|
|
15
|
-
* @type {ArrayBuffer}
|
|
16
|
-
*/
|
|
17
|
-
const reverbBufferBinary = inflateSync(binary).buffer;
|
|
18
|
-
export { reverbBufferBinary };
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
## This is the synthesis engine folder.
|
|
2
|
-
|
|
3
|
-
The code here is responsible for a single midi channel, synthesizing the sound to it.
|
|
4
|
-
|
|
5
|
-
- `engine_methods` contains the methods for the `main_processor.js`
|
|
6
|
-
- `engine_components` contains the various digital signal processing functions such as the wavetable oscillator, low
|
|
7
|
-
pass filter, etc.
|
|
8
|
-
|
|
9
|
-
For those interested, `render_voice.js` file contains the actual DSP synthesis code.
|
|
10
|
-
|
|
11
|
-
`minify_processor.js` uses esbuild to minify the processor code. Importing this instead of `worklet_processor.js` is
|
|
12
|
-
recommended.
|
|
13
|
-
|
|
14
|
-
## How it works in spessasynth_lib
|
|
15
|
-
Both `Synthetizer` and `Sequencer` are essentially "remote control"
|
|
16
|
-
for the actual sequencer and synthesizer in the audio worklet thread (here)
|
|
17
|
-
These core components are wrapped in the AudioWorkletProcessor, which is receiving both commands and data (MIDIs, sound banks)
|
|
18
|
-
through the message port, and sends data back (events, time changes, status changes, etc.).
|
|
19
|
-
|
|
20
|
-
For example,
|
|
21
|
-
the playback to WebMIDIAPI is actually the sequencer in the worklet thread
|
|
22
|
-
playing back the sequence and then postMessaging the commands through the synthesizer to the sequencer
|
|
23
|
-
which actually sends them to the specified output.
|
|
24
|
-
|
|
25
|
-
The wonders of separate audio thread...
|
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
import { getModulatorCurveValue, MOD_PRECOMPUTED_LENGTH } from "./modulator_curves.js";
|
|
2
|
-
import { VolumeEnvelope } from "./volume_envelope.js";
|
|
3
|
-
import { ModulationEnvelope } from "./modulation_envelope.js";
|
|
4
|
-
import { generatorLimits, generatorTypes } from "../../../soundfont/basic_soundfont/generator.js";
|
|
5
|
-
import { Modulator, modulatorSources } from "../../../soundfont/basic_soundfont/modulator.js";
|
|
6
|
-
import { NON_CC_INDEX_OFFSET } from "./controller_tables.js";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* compute_modulator.js
|
|
10
|
-
* purpose: precomputes all curve types and computes modulators
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
const EFFECT_MODULATOR_TRANSFORM_MULTIPLIER = 1000 / 200;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Computes a given modulator
|
|
17
|
-
* @param controllerTable {Int16Array} all midi controllers as 14bit values + the non-controller indexes, starting at 128
|
|
18
|
-
* @param modulator {Modulator} the modulator to compute
|
|
19
|
-
* @param voice {Voice} the voice belonging to the modulator
|
|
20
|
-
* @returns {number} the computed value
|
|
21
|
-
*/
|
|
22
|
-
export function computeModulator(controllerTable, modulator, voice)
|
|
23
|
-
{
|
|
24
|
-
if (modulator.transformAmount === 0)
|
|
25
|
-
{
|
|
26
|
-
modulator.currentValue = 0;
|
|
27
|
-
return 0;
|
|
28
|
-
}
|
|
29
|
-
// mapped to 0-16384
|
|
30
|
-
let rawSourceValue;
|
|
31
|
-
if (modulator.sourceUsesCC)
|
|
32
|
-
{
|
|
33
|
-
rawSourceValue = controllerTable[modulator.sourceIndex];
|
|
34
|
-
}
|
|
35
|
-
else
|
|
36
|
-
{
|
|
37
|
-
const index = modulator.sourceIndex + NON_CC_INDEX_OFFSET;
|
|
38
|
-
switch (modulator.sourceIndex)
|
|
39
|
-
{
|
|
40
|
-
case modulatorSources.noController:
|
|
41
|
-
rawSourceValue = 16383; // equals to 1
|
|
42
|
-
break;
|
|
43
|
-
|
|
44
|
-
case modulatorSources.noteOnKeyNum:
|
|
45
|
-
rawSourceValue = voice.midiNote << 7;
|
|
46
|
-
break;
|
|
47
|
-
|
|
48
|
-
case modulatorSources.noteOnVelocity:
|
|
49
|
-
rawSourceValue = voice.velocity << 7;
|
|
50
|
-
break;
|
|
51
|
-
|
|
52
|
-
case modulatorSources.polyPressure:
|
|
53
|
-
rawSourceValue = voice.pressure << 7;
|
|
54
|
-
break;
|
|
55
|
-
|
|
56
|
-
default:
|
|
57
|
-
rawSourceValue = controllerTable[index]; // pitch bend and range are stored in the cc table
|
|
58
|
-
break;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const sourceValue = transforms[modulator.sourceCurveType][modulator.sourcePolarity][modulator.sourceDirection][rawSourceValue];
|
|
64
|
-
|
|
65
|
-
// mapped to 0-127
|
|
66
|
-
let rawSecondSrcValue;
|
|
67
|
-
if (modulator.secSrcUsesCC)
|
|
68
|
-
{
|
|
69
|
-
rawSecondSrcValue = controllerTable[modulator.secSrcIndex];
|
|
70
|
-
}
|
|
71
|
-
else
|
|
72
|
-
{
|
|
73
|
-
const index = modulator.secSrcIndex + NON_CC_INDEX_OFFSET;
|
|
74
|
-
switch (modulator.secSrcIndex)
|
|
75
|
-
{
|
|
76
|
-
case modulatorSources.noController:
|
|
77
|
-
rawSecondSrcValue = 16383; // equals to 1
|
|
78
|
-
break;
|
|
79
|
-
|
|
80
|
-
case modulatorSources.noteOnKeyNum:
|
|
81
|
-
rawSecondSrcValue = voice.midiNote << 7;
|
|
82
|
-
break;
|
|
83
|
-
|
|
84
|
-
case modulatorSources.noteOnVelocity:
|
|
85
|
-
rawSecondSrcValue = voice.velocity << 7;
|
|
86
|
-
break;
|
|
87
|
-
|
|
88
|
-
case modulatorSources.polyPressure:
|
|
89
|
-
rawSecondSrcValue = voice.pressure << 7;
|
|
90
|
-
break;
|
|
91
|
-
|
|
92
|
-
default:
|
|
93
|
-
rawSecondSrcValue = controllerTable[index]; // pitch bend and range are stored in the cc table
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
const secondSrcValue = transforms[modulator.secSrcCurveType][modulator.secSrcPolarity][modulator.secSrcDirection][rawSecondSrcValue];
|
|
98
|
-
|
|
99
|
-
// see the comment for isEffectModulator (modulator.js in basic_soundfont) for explanation
|
|
100
|
-
let transformAmount = modulator.transformAmount;
|
|
101
|
-
if (modulator.isEffectModulator && transformAmount <= 1000)
|
|
102
|
-
{
|
|
103
|
-
transformAmount *= EFFECT_MODULATOR_TRANSFORM_MULTIPLIER;
|
|
104
|
-
transformAmount = Math.min(transformAmount, 1000);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// compute the modulator
|
|
108
|
-
let computedValue = sourceValue * secondSrcValue * transformAmount;
|
|
109
|
-
|
|
110
|
-
if (modulator.transformType === 2)
|
|
111
|
-
{
|
|
112
|
-
// abs value
|
|
113
|
-
computedValue = Math.abs(computedValue);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
modulator.currentValue = computedValue;
|
|
117
|
-
return computedValue;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Computes modulators of a given voice. Source and index indicate what modulators shall be computed
|
|
122
|
-
* @param voice {Voice} the voice to compute modulators for
|
|
123
|
-
* @param controllerTable {Int16Array} all midi controllers as 14bit values + the non-controller indexes, starting at 128
|
|
124
|
-
* @param sourceUsesCC {number} what modulators should be computed, -1 means all, 0 means modulator source enum 1 means midi controller
|
|
125
|
-
* @param sourceIndex {number} enum for the source
|
|
126
|
-
*/
|
|
127
|
-
export function computeModulators(voice, controllerTable, sourceUsesCC = -1, sourceIndex = 0)
|
|
128
|
-
{
|
|
129
|
-
const modulators = voice.modulators;
|
|
130
|
-
const generators = voice.generators;
|
|
131
|
-
const modulatedGenerators = voice.modulatedGenerators;
|
|
132
|
-
|
|
133
|
-
if (sourceUsesCC === -1)
|
|
134
|
-
{
|
|
135
|
-
// All modulators mode: compute all modulators
|
|
136
|
-
modulatedGenerators.set(generators);
|
|
137
|
-
modulators.forEach(mod =>
|
|
138
|
-
{
|
|
139
|
-
const limits = generatorLimits[mod.modulatorDestination];
|
|
140
|
-
const newValue = modulatedGenerators[mod.modulatorDestination] + computeModulator(
|
|
141
|
-
controllerTable,
|
|
142
|
-
mod,
|
|
143
|
-
voice
|
|
144
|
-
);
|
|
145
|
-
modulatedGenerators[mod.modulatorDestination] = Math.max(
|
|
146
|
-
limits.min,
|
|
147
|
-
Math.min(newValue, limits.max)
|
|
148
|
-
);
|
|
149
|
-
});
|
|
150
|
-
VolumeEnvelope.recalculate(voice);
|
|
151
|
-
ModulationEnvelope.recalculate(voice);
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Optimized mode: calculate only modulators that use the given source
|
|
156
|
-
const volenvNeedsRecalculation = new Set([
|
|
157
|
-
generatorTypes.initialAttenuation,
|
|
158
|
-
generatorTypes.delayVolEnv,
|
|
159
|
-
generatorTypes.attackVolEnv,
|
|
160
|
-
generatorTypes.holdVolEnv,
|
|
161
|
-
generatorTypes.decayVolEnv,
|
|
162
|
-
generatorTypes.sustainVolEnv,
|
|
163
|
-
generatorTypes.releaseVolEnv,
|
|
164
|
-
generatorTypes.keyNumToVolEnvHold,
|
|
165
|
-
generatorTypes.keyNumToVolEnvDecay
|
|
166
|
-
]);
|
|
167
|
-
|
|
168
|
-
const computedDestinations = new Set();
|
|
169
|
-
|
|
170
|
-
modulators.forEach(mod =>
|
|
171
|
-
{
|
|
172
|
-
if (
|
|
173
|
-
(mod.sourceUsesCC === sourceUsesCC && mod.sourceIndex === sourceIndex) ||
|
|
174
|
-
(mod.secSrcUsesCC === sourceUsesCC && mod.secSrcIndex === sourceIndex)
|
|
175
|
-
)
|
|
176
|
-
{
|
|
177
|
-
const destination = mod.modulatorDestination;
|
|
178
|
-
if (!computedDestinations.has(destination))
|
|
179
|
-
{
|
|
180
|
-
// Reset this destination
|
|
181
|
-
modulatedGenerators[destination] = generators[destination];
|
|
182
|
-
// compute our modulator
|
|
183
|
-
computeModulator(controllerTable, mod, voice);
|
|
184
|
-
// sum the values of all modulators for this destination
|
|
185
|
-
modulators.forEach(m =>
|
|
186
|
-
{
|
|
187
|
-
if (m.modulatorDestination === destination)
|
|
188
|
-
{
|
|
189
|
-
const limits = generatorLimits[mod.modulatorDestination];
|
|
190
|
-
const newValue = modulatedGenerators[mod.modulatorDestination] + m.currentValue;
|
|
191
|
-
modulatedGenerators[mod.modulatorDestination] = Math.max(
|
|
192
|
-
limits.min,
|
|
193
|
-
Math.min(newValue, limits.max)
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
computedDestinations.add(destination);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
// Recalculate volume envelope if necessary
|
|
203
|
-
if ([...computedDestinations].some(dest => volenvNeedsRecalculation.has(dest)))
|
|
204
|
-
{
|
|
205
|
-
VolumeEnvelope.recalculate(voice);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
ModulationEnvelope.recalculate(voice);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* as follows: transforms[curveType][polarity][direction] is an array
|
|
214
|
-
* @type {Float32Array[][][]}
|
|
215
|
-
*/
|
|
216
|
-
const transforms = [];
|
|
217
|
-
|
|
218
|
-
for (let curve = 0; curve < 4; curve++)
|
|
219
|
-
{
|
|
220
|
-
transforms[curve] =
|
|
221
|
-
[
|
|
222
|
-
[
|
|
223
|
-
new Float32Array(MOD_PRECOMPUTED_LENGTH),
|
|
224
|
-
new Float32Array(MOD_PRECOMPUTED_LENGTH)
|
|
225
|
-
],
|
|
226
|
-
[
|
|
227
|
-
new Float32Array(MOD_PRECOMPUTED_LENGTH),
|
|
228
|
-
new Float32Array(MOD_PRECOMPUTED_LENGTH)
|
|
229
|
-
]
|
|
230
|
-
];
|
|
231
|
-
for (let i = 0; i < MOD_PRECOMPUTED_LENGTH; i++)
|
|
232
|
-
{
|
|
233
|
-
|
|
234
|
-
// polarity 0 dir 0
|
|
235
|
-
transforms[curve][0][0][i] = getModulatorCurveValue(
|
|
236
|
-
0,
|
|
237
|
-
curve,
|
|
238
|
-
i / MOD_PRECOMPUTED_LENGTH,
|
|
239
|
-
0
|
|
240
|
-
);
|
|
241
|
-
|
|
242
|
-
// polarity 1 dir 0
|
|
243
|
-
transforms[curve][1][0][i] = getModulatorCurveValue(
|
|
244
|
-
0,
|
|
245
|
-
curve,
|
|
246
|
-
i / MOD_PRECOMPUTED_LENGTH,
|
|
247
|
-
1
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
// polarity 0 dir 1
|
|
251
|
-
transforms[curve][0][1][i] = getModulatorCurveValue(
|
|
252
|
-
1,
|
|
253
|
-
curve,
|
|
254
|
-
i / MOD_PRECOMPUTED_LENGTH,
|
|
255
|
-
0
|
|
256
|
-
);
|
|
257
|
-
|
|
258
|
-
// polarity 1 dir 1
|
|
259
|
-
transforms[curve][1][1][i] = getModulatorCurveValue(
|
|
260
|
-
1,
|
|
261
|
-
curve,
|
|
262
|
-
i / MOD_PRECOMPUTED_LENGTH,
|
|
263
|
-
1
|
|
264
|
-
);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { midiControllers } from "../../../midi/midi_message.js";
|
|
2
|
-
import { modulatorSources } from "../../../soundfont/basic_soundfont/modulator.js";
|
|
3
|
-
|
|
4
|
-
/*
|
|
5
|
-
* A bit of explanation:
|
|
6
|
-
* The controller table is stored as an int16 array, it stores 14-bit values.
|
|
7
|
-
* This controller table is then extended with the modulatorSources section,
|
|
8
|
-
* for example, pitch range and pitch range depth.
|
|
9
|
-
* This allows us for precise control range and supports full pitch-wheel resolution.
|
|
10
|
-
*/
|
|
11
|
-
export const NON_CC_INDEX_OFFSET = 128;
|
|
12
|
-
export const CONTROLLER_TABLE_SIZE = 147;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// an array with preset default values, so we can quickly use set() to reset the controllers
|
|
16
|
-
export const resetArray = new Int16Array(CONTROLLER_TABLE_SIZE).fill(0);
|
|
17
|
-
export const setResetValue = (i, v) => resetArray[i] = v << 7;
|
|
18
|
-
|
|
19
|
-
// values come from Falcosoft MidiPlayer 6
|
|
20
|
-
setResetValue(midiControllers.mainVolume, 100);
|
|
21
|
-
setResetValue(midiControllers.balance, 64);
|
|
22
|
-
setResetValue(midiControllers.expressionController, 127);
|
|
23
|
-
setResetValue(midiControllers.pan, 64);
|
|
24
|
-
|
|
25
|
-
setResetValue(midiControllers.portamentoOnOff, 127);
|
|
26
|
-
|
|
27
|
-
setResetValue(midiControllers.filterResonance, 64);
|
|
28
|
-
setResetValue(midiControllers.releaseTime, 64);
|
|
29
|
-
setResetValue(midiControllers.attackTime, 64);
|
|
30
|
-
setResetValue(midiControllers.brightness, 64);
|
|
31
|
-
|
|
32
|
-
setResetValue(midiControllers.decayTime, 64);
|
|
33
|
-
setResetValue(midiControllers.vibratoRate, 64);
|
|
34
|
-
setResetValue(midiControllers.vibratoDepth, 64);
|
|
35
|
-
setResetValue(midiControllers.vibratoDelay, 64);
|
|
36
|
-
setResetValue(midiControllers.generalPurposeController6, 64);
|
|
37
|
-
setResetValue(midiControllers.generalPurposeController8, 64);
|
|
38
|
-
|
|
39
|
-
setResetValue(midiControllers.RPNLsb, 127);
|
|
40
|
-
setResetValue(midiControllers.RPNMsb, 127);
|
|
41
|
-
setResetValue(midiControllers.NRPNLsb, 127);
|
|
42
|
-
setResetValue(midiControllers.NRPNMsb, 127);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
export const PORTAMENTO_CONTROL_UNSET = 1;
|
|
46
|
-
// special case: portamento control
|
|
47
|
-
// since it is only 7-bit, only the values at multiple of 128 are allowed.
|
|
48
|
-
// a value of just 1 indicates no key set, hence no portamento.
|
|
49
|
-
// this is the "initial unset portamento key" flag.
|
|
50
|
-
resetArray[midiControllers.portamentoControl] = PORTAMENTO_CONTROL_UNSET;
|
|
51
|
-
|
|
52
|
-
// pitch wheel
|
|
53
|
-
setResetValue(NON_CC_INDEX_OFFSET + modulatorSources.pitchWheel, 64);
|
|
54
|
-
setResetValue(NON_CC_INDEX_OFFSET + modulatorSources.pitchWheelRange, 2);
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* @enum {number}
|
|
58
|
-
*/
|
|
59
|
-
export const customControllers = {
|
|
60
|
-
channelTuning: 0, // cents, RPN for fine tuning
|
|
61
|
-
channelTransposeFine: 1, // cents, only the decimal tuning, (e.g., transpose is 4.5,
|
|
62
|
-
// then shift by 4 keys + tune by 50 cents)
|
|
63
|
-
modulationMultiplier: 2, // cents, set by modulation depth RPN
|
|
64
|
-
masterTuning: 3, // cents, set by system exclusive
|
|
65
|
-
channelTuningSemitones: 4 // semitones, for RPN coarse tuning
|
|
66
|
-
};
|
|
67
|
-
export const CUSTOM_CONTROLLER_TABLE_SIZE = Object.keys(customControllers).length;
|
|
68
|
-
export const customResetArray = new Float32Array(CUSTOM_CONTROLLER_TABLE_SIZE);
|
|
69
|
-
customResetArray[customControllers.modulationMultiplier] = 1;
|
|
70
|
-
/**
|
|
71
|
-
* @enum {number}
|
|
72
|
-
*/
|
|
73
|
-
export const dataEntryStates = {
|
|
74
|
-
Idle: 0,
|
|
75
|
-
RPCoarse: 1,
|
|
76
|
-
RPFine: 2,
|
|
77
|
-
NRPCoarse: 3,
|
|
78
|
-
NRPFine: 4,
|
|
79
|
-
DataCoarse: 5,
|
|
80
|
-
DataFine: 6
|
|
81
|
-
};
|
|
82
|
-
/**
|
|
83
|
-
* This is a channel configuration enum, it is internally sent from Synthetizer via controller change
|
|
84
|
-
* @enum {number}
|
|
85
|
-
*/
|
|
86
|
-
export const channelConfiguration = {
|
|
87
|
-
velocityOverride: 128 // overrides velocity for the given channel
|
|
88
|
-
};
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A manager for custom key overrides for channels
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export class KeyModifier
|
|
6
|
-
{
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* The new override velocity. -1 means unchanged
|
|
10
|
-
* @type {number}
|
|
11
|
-
*/
|
|
12
|
-
velocity = -1;
|
|
13
|
-
/**
|
|
14
|
-
* The patch this key uses. -1 on either means default
|
|
15
|
-
* @type {{bank: number, program: number}}
|
|
16
|
-
*/
|
|
17
|
-
patch = { bank: -1, program: -1 };
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Linear gain override for the voice
|
|
21
|
-
*/
|
|
22
|
-
gain = 1;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* @param velocity {number}
|
|
26
|
-
* @param bank {number}
|
|
27
|
-
* @param program {number}
|
|
28
|
-
* @param gain {number}
|
|
29
|
-
*/
|
|
30
|
-
constructor(velocity = -1, bank = -1, program = -1, gain = 1)
|
|
31
|
-
{
|
|
32
|
-
this.velocity = velocity;
|
|
33
|
-
this.patch = {
|
|
34
|
-
bank: bank,
|
|
35
|
-
program: program
|
|
36
|
-
};
|
|
37
|
-
this.gain = gain;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* @enum {number}
|
|
43
|
-
*/
|
|
44
|
-
export const workletKeyModifierMessageType = {
|
|
45
|
-
addMapping: 0, // [channel<number>, midiNote<number>, mapping<KeyModifier>]
|
|
46
|
-
deleteMapping: 1, // [channel<number>, midiNote<number>]
|
|
47
|
-
clearMappings: 2 // <no data>
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
export class KeyModifierManager
|
|
51
|
-
{
|
|
52
|
-
/**
|
|
53
|
-
* The velocity override mappings for MIDI keys
|
|
54
|
-
* @type {KeyModifier[][]}
|
|
55
|
-
* @private
|
|
56
|
-
*/
|
|
57
|
-
_keyMappings = [];
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* @param channel {number}
|
|
61
|
-
* @param midiNote {number}
|
|
62
|
-
* @param mapping {KeyModifier}
|
|
63
|
-
*/
|
|
64
|
-
addMapping(channel, midiNote, mapping)
|
|
65
|
-
{
|
|
66
|
-
if (this._keyMappings[channel] === undefined)
|
|
67
|
-
{
|
|
68
|
-
this._keyMappings[channel] = [];
|
|
69
|
-
}
|
|
70
|
-
this._keyMappings[channel][midiNote] = mapping;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
deleteMapping(channel, midiNote)
|
|
74
|
-
{
|
|
75
|
-
if (this._keyMappings[channel]?.[midiNote] === undefined)
|
|
76
|
-
{
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
this._keyMappings[channel][midiNote] = undefined;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
clearMappings()
|
|
83
|
-
{
|
|
84
|
-
this._keyMappings = [];
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* @param mappings {KeyModifier[][]}
|
|
89
|
-
*/
|
|
90
|
-
setMappings(mappings)
|
|
91
|
-
{
|
|
92
|
-
this._keyMappings = mappings;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* @returns {KeyModifier[][]}
|
|
97
|
-
*/
|
|
98
|
-
getMappings()
|
|
99
|
-
{
|
|
100
|
-
return this._keyMappings;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* @param channel {number}
|
|
105
|
-
* @param midiNote {number}
|
|
106
|
-
* @returns {number} velocity, -1 if unchanged
|
|
107
|
-
*/
|
|
108
|
-
getVelocity(channel, midiNote)
|
|
109
|
-
{
|
|
110
|
-
return this._keyMappings[channel]?.[midiNote]?.velocity ?? -1;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* @param channel {number}
|
|
115
|
-
* @param midiNote {number}
|
|
116
|
-
* @returns {number} linear gain
|
|
117
|
-
*/
|
|
118
|
-
getGain(channel, midiNote)
|
|
119
|
-
{
|
|
120
|
-
return this._keyMappings[channel]?.[midiNote]?.gain ?? 1;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* @param channel {number}
|
|
125
|
-
* @param midiNote {number}
|
|
126
|
-
* @returns {boolean}
|
|
127
|
-
*/
|
|
128
|
-
hasOverridePatch(channel, midiNote)
|
|
129
|
-
{
|
|
130
|
-
const bank = this._keyMappings[channel]?.[midiNote]?.patch?.bank;
|
|
131
|
-
return bank !== undefined && bank >= 0;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* @param channel {number}
|
|
136
|
-
* @param midiNote {number}
|
|
137
|
-
* @returns {{bank: number, program: number}} -1 if unchanged
|
|
138
|
-
*/
|
|
139
|
-
getPatch(channel, midiNote)
|
|
140
|
-
{
|
|
141
|
-
const modifier = this._keyMappings[channel]?.[midiNote];
|
|
142
|
-
if (modifier)
|
|
143
|
-
{
|
|
144
|
-
return modifier.patch;
|
|
145
|
-
}
|
|
146
|
-
throw new Error("No modifier.");
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lfo.js
|
|
3
|
-
* purpose: low frequency triangel oscillator
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Calculates a triangular wave value for the given time
|
|
8
|
-
* @param startTime {number} seconds
|
|
9
|
-
* @param frequency {number} Hz
|
|
10
|
-
* @param currentTime {number} seconds
|
|
11
|
-
* @return {number} the value from -1 to 1
|
|
12
|
-
*/
|
|
13
|
-
export function getLFOValue(startTime, frequency, currentTime)
|
|
14
|
-
{
|
|
15
|
-
if (currentTime < startTime)
|
|
16
|
-
{
|
|
17
|
-
return 0;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const xVal = (currentTime - startTime) / (1 / frequency) + 0.25;
|
|
21
|
-
// offset by -0.25, otherwise we start at -1 and can have unexpected jump in pitch or low-pass
|
|
22
|
-
// (happened with Synth Strings 2)
|
|
23
|
-
|
|
24
|
-
// triangle, not sine
|
|
25
|
-
return Math.abs(xVal - (~~(xVal + 0.5))) * 4 - 1;
|
|
26
|
-
}
|