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
package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/wavetable_oscillator.js
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* wavetable_oscillator.js
|
|
3
|
-
* purpose: plays back raw audio data at an arbitrary playback rate
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Fills the output buffer with raw sample data
|
|
9
|
-
* @param voice {WorkletVoice} the voice we're working on
|
|
10
|
-
* @param sampleData {Float32Array} the sample data to write with
|
|
11
|
-
* @param outputBuffer {Float32Array} the output buffer to write to
|
|
12
|
-
*/
|
|
13
|
-
export function getOscillatorData(voice, sampleData, outputBuffer)
|
|
14
|
-
{
|
|
15
|
-
let cur = voice.sample.cursor;
|
|
16
|
-
const loop = (voice.sample.loopingMode === 1) || (voice.sample.loopingMode === 3 && !voice.isInRelease);
|
|
17
|
-
const loopLength = voice.sample.loopEnd - voice.sample.loopStart;
|
|
18
|
-
|
|
19
|
-
if(loop)
|
|
20
|
-
{
|
|
21
|
-
for (let i = 0; i < outputBuffer.length; i++) {
|
|
22
|
-
// check for loop
|
|
23
|
-
while(cur >= voice.sample.loopEnd) {
|
|
24
|
-
cur -= loopLength;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// grab the 2 nearest points
|
|
28
|
-
const floor = ~~cur;
|
|
29
|
-
let ceil = floor + 1;
|
|
30
|
-
|
|
31
|
-
while(ceil >= voice.sample.loopEnd) {
|
|
32
|
-
ceil -= loopLength;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const fraction = cur - floor;
|
|
36
|
-
|
|
37
|
-
// grab the samples and interpolate
|
|
38
|
-
const upper = sampleData[ceil];
|
|
39
|
-
const lower = sampleData[floor];
|
|
40
|
-
outputBuffer[i] = (lower + (upper - lower) * fraction);
|
|
41
|
-
|
|
42
|
-
// commented code because it's probably gonna come handy... (it did like 6 times already :/)
|
|
43
|
-
// if(isNaN(outputBuffer[i]))
|
|
44
|
-
// {
|
|
45
|
-
// console.error(voice, upper, lower, floor, ceil, cur)
|
|
46
|
-
// throw "NAN ALERT";
|
|
47
|
-
// }
|
|
48
|
-
|
|
49
|
-
cur += voice.sample.playbackStep * voice.currentTuningCalculated;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
else
|
|
53
|
-
{
|
|
54
|
-
// check and correct end errors
|
|
55
|
-
if(voice.sample.end >= sampleData.length)
|
|
56
|
-
{
|
|
57
|
-
voice.sample.end = sampleData.length - 1;
|
|
58
|
-
}
|
|
59
|
-
for (let i = 0; i < outputBuffer.length; i++) {
|
|
60
|
-
|
|
61
|
-
// linear interpolation
|
|
62
|
-
const floor = ~~cur;
|
|
63
|
-
const ceil = floor + 1;
|
|
64
|
-
|
|
65
|
-
// flag the voice as finished if needed
|
|
66
|
-
if(ceil >= voice.sample.end)
|
|
67
|
-
{
|
|
68
|
-
voice.finished = true;
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const fraction = cur - floor;
|
|
73
|
-
|
|
74
|
-
// grab the samples and interpolate
|
|
75
|
-
const upper = sampleData[ceil];
|
|
76
|
-
const lower = sampleData[floor];
|
|
77
|
-
outputBuffer[i] = (lower + (upper - lower) * fraction);
|
|
78
|
-
|
|
79
|
-
cur += voice.sample.playbackStep * voice.currentTuningCalculated;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
voice.sample.cursor = cur;
|
|
83
|
-
}
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
import { modulatorSources } from '../../../soundfont/chunk/modulators.js'
|
|
2
|
-
import { getModulatorCurveValue, MOD_PRECOMPUTED_LENGTH } from './modulator_curves.js'
|
|
3
|
-
import { NON_CC_INDEX_OFFSET } from './worklet_processor_channel.js'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* worklet_modulator.js
|
|
7
|
-
* purpose: precomputes all curve types and computes modulators
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Computes a given modulator
|
|
12
|
-
* @param controllerTable {Int16Array} all midi controllers as 14bit values + the non controller indexes, starting at 128
|
|
13
|
-
* @param modulator {Modulator} the modulator to compute
|
|
14
|
-
* @param midiNote {number} the midiNote of the voice belonging to the modulator
|
|
15
|
-
* @param velocity {number} the velocity of the voice belonging to the modulator
|
|
16
|
-
* @returns {number} the computed value
|
|
17
|
-
*/
|
|
18
|
-
export function computeWorkletModulator(controllerTable, modulator, midiNote, velocity)
|
|
19
|
-
{
|
|
20
|
-
if(modulator.transformAmount === 0)
|
|
21
|
-
{
|
|
22
|
-
return 0;
|
|
23
|
-
}
|
|
24
|
-
// mapped to 0-16384
|
|
25
|
-
let rawSourceValue = 0;
|
|
26
|
-
if(modulator.sourceUsesCC)
|
|
27
|
-
{
|
|
28
|
-
rawSourceValue = controllerTable[modulator.sourceIndex];
|
|
29
|
-
}
|
|
30
|
-
else
|
|
31
|
-
{
|
|
32
|
-
const index = modulator.sourceIndex + NON_CC_INDEX_OFFSET;
|
|
33
|
-
switch (modulator.sourceIndex)
|
|
34
|
-
{
|
|
35
|
-
case modulatorSources.noController:
|
|
36
|
-
return 0;// fluid_mod.c line 374 (0 times secondary times amount is still zero)
|
|
37
|
-
|
|
38
|
-
case modulatorSources.noteOnKeyNum:
|
|
39
|
-
rawSourceValue = midiNote << 7;
|
|
40
|
-
break;
|
|
41
|
-
|
|
42
|
-
case modulatorSources.noteOnVelocity:
|
|
43
|
-
case modulatorSources.polyPressure:
|
|
44
|
-
rawSourceValue = velocity << 7;
|
|
45
|
-
break;
|
|
46
|
-
|
|
47
|
-
default:
|
|
48
|
-
rawSourceValue = controllerTable[index]; // use the 7 bit value
|
|
49
|
-
break;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const sourceValue = transforms[modulator.sourceCurveType][modulator.sourcePolarity][modulator.sourceDirection][rawSourceValue];
|
|
55
|
-
|
|
56
|
-
// mapped to 0-127
|
|
57
|
-
let rawSecondSrcValue;
|
|
58
|
-
if(modulator.secSrcUsesCC)
|
|
59
|
-
{
|
|
60
|
-
rawSecondSrcValue = controllerTable[modulator.secSrcIndex];
|
|
61
|
-
}
|
|
62
|
-
else
|
|
63
|
-
{
|
|
64
|
-
const index = modulator.secSrcIndex + NON_CC_INDEX_OFFSET;
|
|
65
|
-
switch (modulator.secSrcIndex)
|
|
66
|
-
{
|
|
67
|
-
case modulatorSources.noController:
|
|
68
|
-
rawSecondSrcValue = 16383;// fluid_mod.c line 376
|
|
69
|
-
break;
|
|
70
|
-
|
|
71
|
-
case modulatorSources.noteOnKeyNum:
|
|
72
|
-
rawSecondSrcValue = midiNote << 7;
|
|
73
|
-
break;
|
|
74
|
-
|
|
75
|
-
case modulatorSources.noteOnVelocity:
|
|
76
|
-
case modulatorSources.polyPressure:
|
|
77
|
-
rawSecondSrcValue = velocity << 7;
|
|
78
|
-
break;
|
|
79
|
-
|
|
80
|
-
default:
|
|
81
|
-
rawSecondSrcValue = controllerTable[index];
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
const secondSrcValue = transforms[modulator.secSrcCurveType][modulator.secSrcPolarity][modulator.secSrcDirection][rawSecondSrcValue];
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
// compute the modulator
|
|
89
|
-
const computedValue = sourceValue * secondSrcValue * modulator.transformAmount;
|
|
90
|
-
|
|
91
|
-
if(modulator.transformType === 2)
|
|
92
|
-
{
|
|
93
|
-
// abs value
|
|
94
|
-
return Math.abs(computedValue);
|
|
95
|
-
}
|
|
96
|
-
return computedValue;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Computes all modulators of a given voice
|
|
101
|
-
* @param voice {WorkletVoice} the voice to compute modulators for
|
|
102
|
-
* @param controllerTable {Int16Array} all midi controllers as 14bit values + the non controller indexes, starting at 128
|
|
103
|
-
*/
|
|
104
|
-
export function computeModulators(voice, controllerTable)
|
|
105
|
-
{
|
|
106
|
-
voice.modulatedGenerators.set(voice.generators);
|
|
107
|
-
voice.modulators.forEach(mod => {
|
|
108
|
-
voice.modulatedGenerators[mod.modulatorDestination] += computeWorkletModulator(controllerTable, mod, voice.midiNote, voice.velocity);
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* as follows: transforms[curveType][polarity][direction] is an array
|
|
114
|
-
* @type {Float32Array[][][]}
|
|
115
|
-
*/
|
|
116
|
-
const transforms = [];
|
|
117
|
-
|
|
118
|
-
for(let curve = 0; curve < 4; curve++)
|
|
119
|
-
{
|
|
120
|
-
transforms[curve] =
|
|
121
|
-
[
|
|
122
|
-
[
|
|
123
|
-
new Float32Array(MOD_PRECOMPUTED_LENGTH),
|
|
124
|
-
new Float32Array(MOD_PRECOMPUTED_LENGTH)
|
|
125
|
-
],
|
|
126
|
-
[
|
|
127
|
-
new Float32Array(MOD_PRECOMPUTED_LENGTH),
|
|
128
|
-
new Float32Array(MOD_PRECOMPUTED_LENGTH)
|
|
129
|
-
]
|
|
130
|
-
];
|
|
131
|
-
for (let i = 0; i < MOD_PRECOMPUTED_LENGTH; i++) {
|
|
132
|
-
|
|
133
|
-
// polarity 0 dir 0
|
|
134
|
-
transforms[curve][0][0][i] = getModulatorCurveValue(
|
|
135
|
-
0,
|
|
136
|
-
curve,
|
|
137
|
-
i / MOD_PRECOMPUTED_LENGTH,
|
|
138
|
-
0);
|
|
139
|
-
if (isNaN(transforms[curve][0][0][i])) {
|
|
140
|
-
transforms[curve][0][0][i] = 1;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// polarity 1 dir 0
|
|
144
|
-
transforms[curve][1][0][i] = getModulatorCurveValue(
|
|
145
|
-
0,
|
|
146
|
-
curve,
|
|
147
|
-
i / MOD_PRECOMPUTED_LENGTH,
|
|
148
|
-
1);
|
|
149
|
-
if (isNaN(transforms[curve][1][0][i])) {
|
|
150
|
-
transforms[curve][1][0][i] = 1;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// polarity 0 dir 1
|
|
154
|
-
transforms[curve][0][1][i] = getModulatorCurveValue(
|
|
155
|
-
1,
|
|
156
|
-
curve,
|
|
157
|
-
i / MOD_PRECOMPUTED_LENGTH,
|
|
158
|
-
0);
|
|
159
|
-
if (isNaN(transforms[curve][0][1][i])) {
|
|
160
|
-
transforms[curve][0][1][i] = 1;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// polarity 1 dir 1
|
|
164
|
-
transforms[curve][1][1][i] = getModulatorCurveValue(
|
|
165
|
-
1,
|
|
166
|
-
curve,
|
|
167
|
-
i / MOD_PRECOMPUTED_LENGTH,
|
|
168
|
-
1);
|
|
169
|
-
if (isNaN(transforms[curve][1][1][i])) {
|
|
170
|
-
transforms[curve][1][1][i] = 1;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { midiControllers } from '../../../midi_parser/midi_message.js'
|
|
2
|
-
import { modulatorSources } from '../../../soundfont/chunk/modulators.js'
|
|
3
|
-
/**
|
|
4
|
-
* @typedef {Object} WorkletProcessorChannel
|
|
5
|
-
* @property {Int16Array} midiControllers - array of MIDI controller values
|
|
6
|
-
* @property {boolean[]} lockedControllers - array indicating if a controller is locked
|
|
7
|
-
* @property {Float32Array} customControllers - array of custom (not sf2) control values such as RPN pitch tuning, transpose, modulation depth, etc.
|
|
8
|
-
* @property {boolean} holdPedal - indicates whether the hold pedal is active
|
|
9
|
-
* @property {boolean} drumChannel - indicates whether the channel is a drum channel
|
|
10
|
-
* @property {dataEntryStates} dataEntryState - the current state of the data entry
|
|
11
|
-
* @property {number} NRPCoarse - the current coarse value of the Non-Registered Parameter
|
|
12
|
-
* @property {number} NRPFine - the current fine value of the Non-Registered Parameter
|
|
13
|
-
* @property {number} RPValue - the current value of the Registered Parameter
|
|
14
|
-
*
|
|
15
|
-
* @property {Preset} preset - the channel's preset
|
|
16
|
-
* @property {boolean} lockPreset - indicates whether the program on the channel is locked
|
|
17
|
-
*
|
|
18
|
-
* @property {boolean} lockVibrato - indicates whether the custom vibrato is locked
|
|
19
|
-
* @property {Object} channelVibrato - vibrato settings for the channel
|
|
20
|
-
* @property {number} channelVibrato.depth - depth of the vibrato effect (cents)
|
|
21
|
-
* @property {number} channelVibrato.delay - delay before the vibrato effect starts (seconds)
|
|
22
|
-
* @property {number} channelVibrato.rate - rate of the vibrato oscillation (Hz)
|
|
23
|
-
|
|
24
|
-
* @property {boolean} isMuted - indicates whether the channel is muted
|
|
25
|
-
* @property {WorkletVoice[]} voices - array of voices currently active on the channel
|
|
26
|
-
* @property {WorkletVoice[]} sustainedVoices - array of voices that are sustained on the channel
|
|
27
|
-
* @property {WorkletVoice[][][]} cachedVoices - first is midi note, second is velocity. output is an array of WorkletVoices
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Adds a new channel
|
|
32
|
-
* @this {Synthesizer}
|
|
33
|
-
*/
|
|
34
|
-
export function addNewChannel()
|
|
35
|
-
{
|
|
36
|
-
/**
|
|
37
|
-
* @type {WorkletProcessorChannel}
|
|
38
|
-
*/
|
|
39
|
-
const channel = {
|
|
40
|
-
midiControllers: new Int16Array(CONTROLLER_TABLE_SIZE),
|
|
41
|
-
lockedControllers: Array(CONTROLLER_TABLE_SIZE).fill(false),
|
|
42
|
-
customControllers: new Float32Array(CUSTOM_CONTROLLER_TABLE_SIZE),
|
|
43
|
-
|
|
44
|
-
NRPCoarse: 0,
|
|
45
|
-
NRPFine: 0,
|
|
46
|
-
RPValue: 0,
|
|
47
|
-
dataEntryState: dataEntryStates.Idle,
|
|
48
|
-
|
|
49
|
-
voices: [],
|
|
50
|
-
sustainedVoices: [],
|
|
51
|
-
cachedVoices: [],
|
|
52
|
-
preset: this.defaultPreset,
|
|
53
|
-
|
|
54
|
-
channelVibrato: {delay: 0, depth: 0, rate: 0},
|
|
55
|
-
lockVibrato: false,
|
|
56
|
-
holdPedal: false,
|
|
57
|
-
isMuted: false,
|
|
58
|
-
drumChannel: false,
|
|
59
|
-
lockPreset: false,
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
for (let i = 0; i < 128; i++) {
|
|
63
|
-
channel.cachedVoices.push([]);
|
|
64
|
-
}
|
|
65
|
-
this.workletProcessorChannels.push(channel);
|
|
66
|
-
this.resetControllers(this.workletProcessorChannels.length - 1);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export const NON_CC_INDEX_OFFSET = 128;
|
|
70
|
-
export const CONTROLLER_TABLE_SIZE = 147;
|
|
71
|
-
// an array with preset default values so we can quickly use set() to reset the controllers
|
|
72
|
-
export const resetArray = new Int16Array(CONTROLLER_TABLE_SIZE);
|
|
73
|
-
// default values (the array is 14 bit so shift the 7 bit values by 7 bits)
|
|
74
|
-
resetArray[midiControllers.mainVolume] = 100 << 7;
|
|
75
|
-
resetArray[midiControllers.expressionController] = 127 << 7;
|
|
76
|
-
resetArray[midiControllers.pan] = 64 << 7;
|
|
77
|
-
resetArray[midiControllers.releaseTime] = 64 << 7;
|
|
78
|
-
resetArray[midiControllers.brightness] = 64 << 7;
|
|
79
|
-
resetArray[midiControllers.effects1Depth] = 40 << 7;
|
|
80
|
-
resetArray[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheel] = 8192;
|
|
81
|
-
resetArray[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheelRange] = 2 << 7;
|
|
82
|
-
resetArray[NON_CC_INDEX_OFFSET + modulatorSources.channelPressure] = 127 << 7;
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* @enum {number}
|
|
86
|
-
*/
|
|
87
|
-
export const dataEntryStates = {
|
|
88
|
-
Idle: 0,
|
|
89
|
-
RPCoarse: 1,
|
|
90
|
-
RPFine: 2,
|
|
91
|
-
NRPCoarse: 3,
|
|
92
|
-
NRPFine: 4,
|
|
93
|
-
DataCoarse: 5,
|
|
94
|
-
DataFine: 6
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
export const customControllers = {
|
|
99
|
-
channelTuning: 0, // cents
|
|
100
|
-
channelTranspose: 1, // cents
|
|
101
|
-
modulationMultiplier: 2, // cents
|
|
102
|
-
masterTuning: 3, // cents
|
|
103
|
-
}
|
|
104
|
-
export const CUSTOM_CONTROLLER_TABLE_SIZE = Object.keys(customControllers).length;
|
|
105
|
-
export const customResetArray = new Float32Array(CUSTOM_CONTROLLER_TABLE_SIZE);
|
|
106
|
-
customResetArray[customControllers.modulationMultiplier] = 1;
|
|
@@ -1,313 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* worklet_voice.js
|
|
3
|
-
* purpose: prepares workletvoices from sample and generator data and manages sample dumping
|
|
4
|
-
* note: sample dumping means sending it over to the AudioWorkletGlobalScope
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @typedef {Object} WorkletSample
|
|
9
|
-
* @property {number} sampleID - ID of the sample
|
|
10
|
-
* @property {number} playbackStep - current playback step (rate)
|
|
11
|
-
* @property {number} cursor - current position in the sample
|
|
12
|
-
* @property {number} rootKey - root key of the sample
|
|
13
|
-
* @property {number} loopStart - start position of the loop
|
|
14
|
-
* @property {number} loopEnd - end position of the loop
|
|
15
|
-
* @property {number} end - end position of the sample
|
|
16
|
-
* @property {0|1|2} loopingMode - looping mode of the sample
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* @typedef {Object} WorkletLowpassFilter
|
|
21
|
-
* @property {number} a0 - filter coefficient 1
|
|
22
|
-
* @property {number} a1 - filter coefficient 2
|
|
23
|
-
* @property {number} a2 - filter coefficient 3
|
|
24
|
-
* @property {number} a3 - filter coefficient 4
|
|
25
|
-
* @property {number} a4 - filter coefficient 5
|
|
26
|
-
* @property {number} x1 - input history 1
|
|
27
|
-
* @property {number} x2 - input history 2
|
|
28
|
-
* @property {number} y1 - output history 1
|
|
29
|
-
* @property {number} y2 - output history 2
|
|
30
|
-
* @property {number} reasonanceCb - reasonance in centibels
|
|
31
|
-
* @property {number} reasonanceGain - resonance gain
|
|
32
|
-
* @property {number} cutoffCents - cutoff frequency in cents
|
|
33
|
-
* @property {number} cutoffHz - cutoff frequency in Hz
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* @typedef {Object} WorkletVoice
|
|
38
|
-
* @property {WorkletSample} sample - sample ID for voice.
|
|
39
|
-
* @property {WorkletLowpassFilter} filter - lowpass filter applied to the voice
|
|
40
|
-
* @property {Int16Array} generators - the unmodulated (constant) generators of the voice
|
|
41
|
-
* @property {Modulator[]} modulators - the voice's modulators
|
|
42
|
-
* @property {Int16Array} modulatedGenerators - the generators modulated by the modulators
|
|
43
|
-
*
|
|
44
|
-
* @property {boolean} finished - indicates if the voice has finished
|
|
45
|
-
* @property {boolean} isInRelease - indicates if the voice is in the release phase
|
|
46
|
-
*
|
|
47
|
-
* @property {number} channelNumber - MIDI channel number
|
|
48
|
-
* @property {number} velocity - velocity of the note
|
|
49
|
-
* @property {number} midiNote - MIDI note number
|
|
50
|
-
* @property {number} targetKey - target key for the note
|
|
51
|
-
*
|
|
52
|
-
* @property {number} currentAttenuationDb - current attenuation in dB (used for calculating start of the release phase)
|
|
53
|
-
* @property {0|1|2|3|4} volumeEnvelopeState - state of the volume envelope.
|
|
54
|
-
* @property {number} currentModEnvValue - current value of the modulation envelope
|
|
55
|
-
*
|
|
56
|
-
* @property {number} startTime - start time of the voice
|
|
57
|
-
* @property {number} releaseStartTime - start time of the release phase
|
|
58
|
-
* @property {number} releaseStartModEnv - modenv value at the start of the release phase
|
|
59
|
-
*
|
|
60
|
-
* @property {number} currentTuningCents - current tuning adjustment in cents
|
|
61
|
-
* @property {number} currentTuningCalculated - calculated tuning adjustment
|
|
62
|
-
* @property {number} currentPan - from 0 to 1
|
|
63
|
-
*/
|
|
64
|
-
|
|
65
|
-
import { addAndClampGenerator, generatorTypes } from '../../../soundfont/chunk/generators.js'
|
|
66
|
-
import { SpessaSynthTable } from '../../../utils/loggin.js'
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* the sampleID is the index
|
|
71
|
-
* @type {boolean[]}
|
|
72
|
-
*/
|
|
73
|
-
let globalDumpedSamplesList = [];
|
|
74
|
-
|
|
75
|
-
export function clearSamplesList()
|
|
76
|
-
{
|
|
77
|
-
globalDumpedSamplesList = [];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function /**
|
|
81
|
-
* This is how the logic works: since sf3 is compressed, we rely on an async decoder.
|
|
82
|
-
* So, if the sample isn't loaded yet:
|
|
83
|
-
* send the workletVoice (generators, modulators, etc) and the WorkletSample(sampleID + end offset + loop)
|
|
84
|
-
* once the voice is done, then we dump it.
|
|
85
|
-
*
|
|
86
|
-
* on the WorkletScope side:
|
|
87
|
-
* skip the voice if sampleID isn't valid
|
|
88
|
-
* once we receive a sample dump, adjust all voice endOffsets (loop is already correct in sf3)
|
|
89
|
-
* now the voice starts playing, yay!
|
|
90
|
-
* @param channel {number} channel hint for the processor to recalculate cursor positions
|
|
91
|
-
* @param sample {Sample}
|
|
92
|
-
* @param id {number}
|
|
93
|
-
* @param sampleDumpCallback {function({channel: number, sampleID: number, sampleData: Float32Array})}
|
|
94
|
-
*/
|
|
95
|
-
dumpSample(channel, sample, id, sampleDumpCallback)
|
|
96
|
-
{
|
|
97
|
-
// flag as defined, so it's currently being dumped
|
|
98
|
-
globalDumpedSamplesList[id] = false;
|
|
99
|
-
|
|
100
|
-
// load the data
|
|
101
|
-
sampleDumpCallback({
|
|
102
|
-
channel: channel,
|
|
103
|
-
sampleID: id,
|
|
104
|
-
sampleData: sample.getAudioData()
|
|
105
|
-
});
|
|
106
|
-
globalDumpedSamplesList[id] = true;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Deep clone function for the WorkletVoice object and its nested structures.
|
|
111
|
-
* This function handles Int16Array, objects, arrays, and primitives.
|
|
112
|
-
* It does not handle circular references.
|
|
113
|
-
* @param {WorkletVoice} obj - The object to clone.
|
|
114
|
-
* @returns {WorkletVoice} - Cloned object.
|
|
115
|
-
*/
|
|
116
|
-
function deepClone(obj) {
|
|
117
|
-
if (obj === null || typeof obj !== 'object') {
|
|
118
|
-
return obj;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Handle Int16Array separately
|
|
122
|
-
if (obj instanceof Int16Array) {
|
|
123
|
-
return new Int16Array(obj);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Handle objects and arrays
|
|
127
|
-
const clonedObj = Array.isArray(obj) ? [] : {};
|
|
128
|
-
for (let key in obj) {
|
|
129
|
-
if (obj.hasOwnProperty(key)) {
|
|
130
|
-
if (typeof obj[key] === 'object' && obj[key] !== null) {
|
|
131
|
-
clonedObj[key] = deepClone(obj[key]); // Recursively clone nested objects
|
|
132
|
-
} else if (obj[key] instanceof Int16Array) {
|
|
133
|
-
clonedObj[key] = new Int16Array(obj[key]); // Clone Int16Array
|
|
134
|
-
} else {
|
|
135
|
-
clonedObj[key] = obj[key]; // Copy primitives
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return clonedObj;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* @param channel {number} a hint for the processor to recalculate sample cursors when sample dumping
|
|
145
|
-
* @param midiNote {number}
|
|
146
|
-
* @param velocity {number}
|
|
147
|
-
* @param preset {Preset}
|
|
148
|
-
* @param currentTime {number}
|
|
149
|
-
* @param sampleRate {number}
|
|
150
|
-
* @param sampleDumpCallback {function({channel: number, sampleID: number, sampleData: Float32Array})}
|
|
151
|
-
* @param cachedVoices {WorkletVoice[][][]} first is midi note, second is velocity. output is an array of WorkletVoices
|
|
152
|
-
* @param debug {boolean}
|
|
153
|
-
* @returns {WorkletVoice[]}
|
|
154
|
-
*/
|
|
155
|
-
export function getWorkletVoices(channel,
|
|
156
|
-
midiNote,
|
|
157
|
-
velocity,
|
|
158
|
-
preset,
|
|
159
|
-
currentTime,
|
|
160
|
-
sampleRate,
|
|
161
|
-
sampleDumpCallback,
|
|
162
|
-
cachedVoices,
|
|
163
|
-
debug=false)
|
|
164
|
-
{
|
|
165
|
-
/**
|
|
166
|
-
* @type {WorkletVoice[]}
|
|
167
|
-
*/
|
|
168
|
-
let workletVoices;
|
|
169
|
-
|
|
170
|
-
const cached = cachedVoices[midiNote][velocity];
|
|
171
|
-
if(cached)
|
|
172
|
-
{
|
|
173
|
-
workletVoices = cached.map(deepClone);
|
|
174
|
-
workletVoices.forEach(v => {
|
|
175
|
-
v.startTime = currentTime;
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
else
|
|
179
|
-
{
|
|
180
|
-
let canCache = true;
|
|
181
|
-
/**
|
|
182
|
-
* @returns {WorkletVoice}
|
|
183
|
-
*/
|
|
184
|
-
workletVoices = preset.getSamplesAndGenerators(midiNote, velocity).map(sampleAndGenerators => {
|
|
185
|
-
// dump the sample if haven't already
|
|
186
|
-
if (globalDumpedSamplesList[sampleAndGenerators.sampleID] !== true) {
|
|
187
|
-
// if the sample is currently being loaded, don't dump again (undefined means not loaded, false means is being loaded)
|
|
188
|
-
if(globalDumpedSamplesList[sampleAndGenerators.sampleID] === undefined) {
|
|
189
|
-
dumpSample(channel, sampleAndGenerators.sample, sampleAndGenerators.sampleID, sampleDumpCallback);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// can't cache the voice as the end in workletSample maybe is incorrect (the sample is still loading)
|
|
193
|
-
if(globalDumpedSamplesList[sampleAndGenerators.sampleID] !== true)
|
|
194
|
-
{
|
|
195
|
-
canCache = false;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// create the generator list
|
|
200
|
-
const generators = new Int16Array(60);
|
|
201
|
-
// apply and sum the gens
|
|
202
|
-
for (let i = 0; i < 60; i++) {
|
|
203
|
-
generators[i] = addAndClampGenerator(i, sampleAndGenerators.presetGenerators, sampleAndGenerators.instrumentGenerators);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// !! EMU initial attenuation correction, multiply initial attenuation by 0.4
|
|
207
|
-
generators[generatorTypes.initialAttenuation] = Math.floor(generators[generatorTypes.initialAttenuation] * 0.4);
|
|
208
|
-
|
|
209
|
-
// key override
|
|
210
|
-
let rootKey = sampleAndGenerators.sample.samplePitch;
|
|
211
|
-
if (generators[generatorTypes.overridingRootKey] > -1) {
|
|
212
|
-
rootKey = generators[generatorTypes.overridingRootKey];
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
let targetKey = midiNote;
|
|
216
|
-
if (generators[generatorTypes.keyNum] > -1) {
|
|
217
|
-
targetKey = generators[generatorTypes.keyNum];
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// determine looping mode now. if the loop is too small, disable
|
|
221
|
-
const loopStart = (sampleAndGenerators.sample.sampleLoopStartIndex / 2) + (generators[generatorTypes.startloopAddrsOffset] + (generators[generatorTypes.startloopAddrsCoarseOffset] * 32768));
|
|
222
|
-
const loopEnd = (sampleAndGenerators.sample.sampleLoopEndIndex / 2) + (generators[generatorTypes.endloopAddrsOffset] + (generators[generatorTypes.endloopAddrsCoarseOffset] * 32768));
|
|
223
|
-
let loopingMode = generators[generatorTypes.sampleModes];
|
|
224
|
-
if (loopEnd - loopStart < 1) {
|
|
225
|
-
loopingMode = 0;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// determine end
|
|
229
|
-
/**
|
|
230
|
-
* create the worklet sample
|
|
231
|
-
* @type {WorkletSample}
|
|
232
|
-
*/
|
|
233
|
-
const workletSample = {
|
|
234
|
-
sampleID: sampleAndGenerators.sampleID,
|
|
235
|
-
playbackStep: (sampleAndGenerators.sample.sampleRate / sampleRate) * Math.pow(2, sampleAndGenerators.sample.samplePitchCorrection / 1200),// cent tuning
|
|
236
|
-
cursor: generators[generatorTypes.startAddrsOffset] + (generators[generatorTypes.startAddrsCoarseOffset] * 32768),
|
|
237
|
-
rootKey: rootKey,
|
|
238
|
-
loopStart: loopStart,
|
|
239
|
-
loopEnd: loopEnd,
|
|
240
|
-
end: Math.floor( sampleAndGenerators.sample.sampleData.length) - 1 + (generators[generatorTypes.endAddrOffset] + (generators[generatorTypes.endAddrsCoarseOffset] * 32768)),
|
|
241
|
-
loopingMode: loopingMode
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
// velocity override
|
|
245
|
-
if (generators[generatorTypes.velocity] > -1) {
|
|
246
|
-
velocity = generators[generatorTypes.velocity];
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
if(debug)
|
|
250
|
-
{
|
|
251
|
-
SpessaSynthTable([{
|
|
252
|
-
Sample: sampleAndGenerators.sample.sampleName,
|
|
253
|
-
Generators: generators,
|
|
254
|
-
Modulators: sampleAndGenerators.modulators.map(m => m.debugString()),
|
|
255
|
-
Velocity: velocity,
|
|
256
|
-
TargetKey: targetKey,
|
|
257
|
-
MidiNote: midiNote,
|
|
258
|
-
WorkletSample: workletSample
|
|
259
|
-
}]);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return {
|
|
263
|
-
filter: {
|
|
264
|
-
a0: 0,
|
|
265
|
-
a1: 0,
|
|
266
|
-
a2: 0,
|
|
267
|
-
a3: 0,
|
|
268
|
-
a4: 0,
|
|
269
|
-
|
|
270
|
-
x1: 0,
|
|
271
|
-
x2: 0,
|
|
272
|
-
y1: 0,
|
|
273
|
-
y2: 0,
|
|
274
|
-
reasonanceCb: 0,
|
|
275
|
-
reasonanceGain: 1,
|
|
276
|
-
cutoffCents: 13500,
|
|
277
|
-
cutoffHz: 20000
|
|
278
|
-
},
|
|
279
|
-
// generators and modulators
|
|
280
|
-
generators: generators,
|
|
281
|
-
modulators: sampleAndGenerators.modulators,
|
|
282
|
-
modulatedGenerators: new Int16Array(60),
|
|
283
|
-
|
|
284
|
-
// sample and playback data
|
|
285
|
-
sample: workletSample,
|
|
286
|
-
velocity: velocity,
|
|
287
|
-
midiNote: midiNote,
|
|
288
|
-
channelNumber: channel,
|
|
289
|
-
startTime: currentTime,
|
|
290
|
-
targetKey: targetKey,
|
|
291
|
-
currentTuningCalculated: 1,
|
|
292
|
-
currentTuningCents: 0,
|
|
293
|
-
releaseStartTime: Infinity,
|
|
294
|
-
|
|
295
|
-
// envelope data
|
|
296
|
-
finished: false,
|
|
297
|
-
isInRelease: false,
|
|
298
|
-
currentAttenuationDb: 100,
|
|
299
|
-
currentModEnvValue: 0,
|
|
300
|
-
releaseStartModEnv: 1,
|
|
301
|
-
volumeEnvelopeState: 0,
|
|
302
|
-
currentPan: 0.5
|
|
303
|
-
};
|
|
304
|
-
|
|
305
|
-
});
|
|
306
|
-
// cache the voice
|
|
307
|
-
if(canCache) {
|
|
308
|
-
// clone it so the system won't mess with it!
|
|
309
|
-
cachedVoices[midiNote][velocity] = workletVoices.map(deepClone);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
return workletVoices;
|
|
313
|
-
}
|