spessasynth_core 3.27.8 → 4.0.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 +85 -51
- package/dist/index.d.ts +4057 -0
- package/dist/index.js +17188 -0
- package/dist/index.js.map +1 -0
- package/package.json +23 -6
- package/index.js +0 -132
- package/src/externals/README.md +0 -6
- package/src/externals/fflate/LICENSE +0 -21
- package/src/externals/fflate/fflate.min.js +0 -1
- package/src/externals/stbvorbis_sync/@types/stbvorbis_sync.d.ts +0 -12
- package/src/externals/stbvorbis_sync/LICENSE +0 -202
- package/src/externals/stbvorbis_sync/NOTICE +0 -6
- package/src/externals/stbvorbis_sync/stbvorbis_sync.min.js +0 -1
- package/src/midi/README.md +0 -32
- package/src/midi/basic_midi.js +0 -587
- package/src/midi/midi_builder.js +0 -203
- package/src/midi/midi_loader.js +0 -321
- package/src/midi/midi_message.js +0 -254
- package/src/midi/midi_sequence.js +0 -230
- package/src/midi/midi_tools/get_note_times.js +0 -154
- package/src/midi/midi_tools/midi_editor.js +0 -611
- package/src/midi/midi_tools/midi_writer.js +0 -105
- package/src/midi/midi_tools/rmidi_writer.js +0 -566
- package/src/midi/midi_tools/used_keys_loaded.js +0 -256
- package/src/midi/xmf_loader.js +0 -454
- package/src/sequencer/README.md +0 -9
- package/src/sequencer/events.js +0 -81
- package/src/sequencer/play.js +0 -362
- package/src/sequencer/process_event.js +0 -165
- package/src/sequencer/process_tick.js +0 -104
- package/src/sequencer/sequencer_engine.js +0 -372
- package/src/sequencer/song_control.js +0 -196
- package/src/soundfont/README.md +0 -11
- package/src/soundfont/basic_soundfont/basic_global_zone.js +0 -6
- package/src/soundfont/basic_soundfont/basic_instrument.js +0 -115
- package/src/soundfont/basic_soundfont/basic_instrument_zone.js +0 -45
- package/src/soundfont/basic_soundfont/basic_preset.js +0 -313
- package/src/soundfont/basic_soundfont/basic_preset_zone.js +0 -39
- package/src/soundfont/basic_soundfont/basic_sample.js +0 -477
- package/src/soundfont/basic_soundfont/basic_soundbank.js +0 -740
- package/src/soundfont/basic_soundfont/basic_zone.js +0 -145
- package/src/soundfont/basic_soundfont/generator.js +0 -76
- package/src/soundfont/basic_soundfont/generator_types.js +0 -151
- package/src/soundfont/basic_soundfont/modulator.js +0 -581
- package/src/soundfont/basic_soundfont/riff_chunk.js +0 -195
- package/src/soundfont/basic_soundfont/write_dls/art2.js +0 -174
- package/src/soundfont/basic_soundfont/write_dls/articulator.js +0 -49
- package/src/soundfont/basic_soundfont/write_dls/combine_zones.js +0 -374
- package/src/soundfont/basic_soundfont/write_dls/ins.js +0 -85
- package/src/soundfont/basic_soundfont/write_dls/lins.js +0 -15
- package/src/soundfont/basic_soundfont/write_dls/modulator_converter.js +0 -330
- package/src/soundfont/basic_soundfont/write_dls/rgn2.js +0 -120
- package/src/soundfont/basic_soundfont/write_dls/wave.js +0 -71
- package/src/soundfont/basic_soundfont/write_dls/write_dls.js +0 -124
- package/src/soundfont/basic_soundfont/write_dls/wsmp.js +0 -78
- package/src/soundfont/basic_soundfont/write_dls/wvpl.js +0 -35
- package/src/soundfont/basic_soundfont/write_sf2/ibag.js +0 -60
- package/src/soundfont/basic_soundfont/write_sf2/igen.js +0 -91
- package/src/soundfont/basic_soundfont/write_sf2/imod.js +0 -62
- package/src/soundfont/basic_soundfont/write_sf2/inst.js +0 -42
- package/src/soundfont/basic_soundfont/write_sf2/pbag.js +0 -57
- package/src/soundfont/basic_soundfont/write_sf2/pgen.js +0 -92
- package/src/soundfont/basic_soundfont/write_sf2/phdr.js +0 -61
- package/src/soundfont/basic_soundfont/write_sf2/pmod.js +0 -62
- package/src/soundfont/basic_soundfont/write_sf2/sdta.js +0 -131
- package/src/soundfont/basic_soundfont/write_sf2/shdr.js +0 -77
- package/src/soundfont/basic_soundfont/write_sf2/write.js +0 -287
- package/src/soundfont/dls/articulator_converter.js +0 -402
- package/src/soundfont/dls/dls_destinations.js +0 -38
- package/src/soundfont/dls/dls_instrument.js +0 -20
- package/src/soundfont/dls/dls_preset.js +0 -43
- package/src/soundfont/dls/dls_sample.js +0 -238
- package/src/soundfont/dls/dls_soundfont.js +0 -183
- package/src/soundfont/dls/dls_sources.js +0 -63
- package/src/soundfont/dls/dls_zone.js +0 -89
- package/src/soundfont/dls/read_articulation.js +0 -300
- package/src/soundfont/dls/read_instrument.js +0 -118
- package/src/soundfont/dls/read_instrument_list.js +0 -17
- package/src/soundfont/dls/read_lart.js +0 -35
- package/src/soundfont/dls/read_region.js +0 -157
- package/src/soundfont/dls/read_samples.js +0 -154
- package/src/soundfont/load_soundfont.js +0 -21
- package/src/soundfont/read_sf2/generators.js +0 -43
- package/src/soundfont/read_sf2/instrument_zones.js +0 -75
- package/src/soundfont/read_sf2/instruments.js +0 -71
- package/src/soundfont/read_sf2/modulators.js +0 -25
- package/src/soundfont/read_sf2/preset_zones.js +0 -79
- package/src/soundfont/read_sf2/presets.js +0 -80
- package/src/soundfont/read_sf2/samples.js +0 -317
- package/src/soundfont/read_sf2/soundfont.js +0 -452
- package/src/soundfont/read_sf2/zones.js +0 -28
- package/src/synthetizer/README.md +0 -7
- package/src/synthetizer/audio_engine/README.md +0 -9
- package/src/synthetizer/audio_engine/engine_components/compute_modulator.js +0 -289
- package/src/synthetizer/audio_engine/engine_components/controller_tables.js +0 -90
- package/src/synthetizer/audio_engine/engine_components/dynamic_modulator_system.js +0 -95
- package/src/synthetizer/audio_engine/engine_components/enums.js +0 -18
- package/src/synthetizer/audio_engine/engine_components/key_modifier_manager.js +0 -151
- package/src/synthetizer/audio_engine/engine_components/lfo.js +0 -26
- package/src/synthetizer/audio_engine/engine_components/lowpass_filter.js +0 -282
- package/src/synthetizer/audio_engine/engine_components/midi_audio_channel.js +0 -551
- package/src/synthetizer/audio_engine/engine_components/modulation_envelope.js +0 -181
- package/src/synthetizer/audio_engine/engine_components/modulator_curves.js +0 -89
- package/src/synthetizer/audio_engine/engine_components/soundfont_manager.js +0 -265
- package/src/synthetizer/audio_engine/engine_components/stereo_panner.js +0 -124
- package/src/synthetizer/audio_engine/engine_components/unit_converter.js +0 -73
- package/src/synthetizer/audio_engine/engine_components/voice.js +0 -525
- package/src/synthetizer/audio_engine/engine_components/volume_envelope.js +0 -402
- package/src/synthetizer/audio_engine/engine_components/wavetable_oscillator.js +0 -274
- package/src/synthetizer/audio_engine/engine_methods/controller_control/controller_change.js +0 -159
- package/src/synthetizer/audio_engine/engine_methods/controller_control/master_parameters.js +0 -53
- package/src/synthetizer/audio_engine/engine_methods/controller_control/reset_controllers.js +0 -254
- package/src/synthetizer/audio_engine/engine_methods/create_midi_channel.js +0 -20
- package/src/synthetizer/audio_engine/engine_methods/data_entry/awe32.js +0 -198
- package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_coarse.js +0 -281
- package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_fine.js +0 -109
- package/src/synthetizer/audio_engine/engine_methods/mute_channel.js +0 -17
- package/src/synthetizer/audio_engine/engine_methods/note_on.js +0 -220
- package/src/synthetizer/audio_engine/engine_methods/portamento_time.js +0 -92
- package/src/synthetizer/audio_engine/engine_methods/program_change.js +0 -35
- package/src/synthetizer/audio_engine/engine_methods/render_voice.js +0 -214
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/embedded_sound_bank.js +0 -42
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/get_preset.js +0 -0
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/update_preset_list.js +0 -19
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/kill_note.js +0 -23
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/note_off.js +0 -56
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_channels.js +0 -16
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_notes.js +0 -30
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/voice_killing.js +0 -63
- package/src/synthetizer/audio_engine/engine_methods/system_exclusive.js +0 -1058
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/channel_pressure.js +0 -23
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/pitch_wheel.js +0 -31
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/poly_pressure.js +0 -29
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_master_tuning.js +0 -15
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_modulation_depth.js +0 -26
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_octave_tuning.js +0 -19
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_tuning.js +0 -27
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_all_channels.js +0 -15
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_channel.js +0 -34
- package/src/synthetizer/audio_engine/main_processor.js +0 -817
- package/src/synthetizer/audio_engine/snapshot/apply_synthesizer_snapshot.js +0 -16
- package/src/synthetizer/audio_engine/snapshot/channel_snapshot.js +0 -175
- package/src/synthetizer/audio_engine/snapshot/synthesizer_snapshot.js +0 -116
- package/src/synthetizer/audio_engine/synth_processor_options.js +0 -18
- package/src/synthetizer/synth_constants.js +0 -26
- package/src/utils/README.md +0 -8
- package/src/utils/buffer_to_wav.js +0 -197
- package/src/utils/byte_functions/big_endian.js +0 -32
- package/src/utils/byte_functions/little_endian.js +0 -77
- package/src/utils/byte_functions/string.js +0 -92
- package/src/utils/byte_functions/variable_length_quantity.js +0 -42
- package/src/utils/fill_with_defaults.js +0 -21
- package/src/utils/indexed_array.js +0 -34
- package/src/utils/loggin.js +0 -71
- package/src/utils/other.js +0 -92
- package/src/utils/sysex_detector.js +0 -58
- package/src/utils/xg_hacks.js +0 -193
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* unit_converter.js
|
|
3
|
-
* purpose: converts soundfont units into more useable values with the use of lookup tables to improve performance
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
// timecent lookup table
|
|
8
|
-
const MIN_TIMECENT = -15000;
|
|
9
|
-
const MAX_TIMECENT = 15000;
|
|
10
|
-
const timecentLookupTable = new Float32Array(MAX_TIMECENT - MIN_TIMECENT + 1);
|
|
11
|
-
for (let i = 0; i < timecentLookupTable.length; i++)
|
|
12
|
-
{
|
|
13
|
-
const timecents = MIN_TIMECENT + i;
|
|
14
|
-
timecentLookupTable[i] = Math.pow(2, timecents / 1200);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Converts timecents to seconds
|
|
19
|
-
* @param timecents {number} timecents
|
|
20
|
-
* @returns {number} seconds
|
|
21
|
-
*/
|
|
22
|
-
export function timecentsToSeconds(timecents)
|
|
23
|
-
{
|
|
24
|
-
if (timecents <= -32767)
|
|
25
|
-
{
|
|
26
|
-
return 0;
|
|
27
|
-
}
|
|
28
|
-
return timecentLookupTable[timecents - MIN_TIMECENT];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// abs cent lookup table
|
|
32
|
-
const MIN_ABS_CENT = -20000; // freqVibLfo
|
|
33
|
-
const MAX_ABS_CENT = 16500; // filterFc
|
|
34
|
-
const absoluteCentLookupTable = new Float32Array(MAX_ABS_CENT - MIN_ABS_CENT + 1);
|
|
35
|
-
for (let i = 0; i < absoluteCentLookupTable.length; i++)
|
|
36
|
-
{
|
|
37
|
-
const absoluteCents = MIN_ABS_CENT + i;
|
|
38
|
-
absoluteCentLookupTable[i] = 440 * Math.pow(2, (absoluteCents - 6900) / 1200);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Converts absolute cents to hertz
|
|
43
|
-
* @param cents {number} absolute cents
|
|
44
|
-
* @returns {number} hertz
|
|
45
|
-
*/
|
|
46
|
-
export function absCentsToHz(cents)
|
|
47
|
-
{
|
|
48
|
-
if (cents < MIN_ABS_CENT || cents > MAX_ABS_CENT)
|
|
49
|
-
{
|
|
50
|
-
return 440 * Math.pow(2, (cents - 6900) / 1200);
|
|
51
|
-
}
|
|
52
|
-
return absoluteCentLookupTable[~~(cents) - MIN_ABS_CENT];
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// decibel lookup table (2 points of precision)
|
|
56
|
-
const MIN_DECIBELS = -1660;
|
|
57
|
-
const MAX_DECIBELS = 1600;
|
|
58
|
-
const decibelLookUpTable = new Float32Array((MAX_DECIBELS - MIN_DECIBELS) * 100 + 1);
|
|
59
|
-
for (let i = 0; i < decibelLookUpTable.length; i++)
|
|
60
|
-
{
|
|
61
|
-
const decibels = (MIN_DECIBELS * 100 + i) / 100;
|
|
62
|
-
decibelLookUpTable[i] = Math.pow(10, -decibels / 20);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* convers decibel attenuation to gain
|
|
67
|
-
* @param decibels {number} the decibel attenuation
|
|
68
|
-
* @returns {number} gain
|
|
69
|
-
*/
|
|
70
|
-
export function decibelAttenuationToGain(decibels)
|
|
71
|
-
{
|
|
72
|
-
return decibelLookUpTable[Math.floor((decibels - MIN_DECIBELS) * 100)];
|
|
73
|
-
}
|
|
@@ -1,525 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* voice.js
|
|
3
|
-
* purpose: prepares Voices from sample and generator data
|
|
4
|
-
*/
|
|
5
|
-
import { MIN_EXCLUSIVE_LENGTH, MIN_NOTE_LENGTH } from "../main_processor.js";
|
|
6
|
-
import { SpessaSynthWarn } from "../../../utils/loggin.js";
|
|
7
|
-
import { LowpassFilter } from "./lowpass_filter.js";
|
|
8
|
-
import { VolumeEnvelope } from "./volume_envelope.js";
|
|
9
|
-
import { ModulationEnvelope } from "./modulation_envelope.js";
|
|
10
|
-
import { addAndClampGenerator } from "../../../soundfont/basic_soundfont/generator.js";
|
|
11
|
-
import { Modulator } from "../../../soundfont/basic_soundfont/modulator.js";
|
|
12
|
-
import { GENERATORS_AMOUNT, generatorTypes } from "../../../soundfont/basic_soundfont/generator_types.js";
|
|
13
|
-
|
|
14
|
-
const EXCLUSIVE_CUTOFF_TIME = -2320;
|
|
15
|
-
const EXCLUSIVE_MOD_CUTOFF_TIME = -1130; // less because filter shenanigans
|
|
16
|
-
|
|
17
|
-
class AudioSample
|
|
18
|
-
{
|
|
19
|
-
/**
|
|
20
|
-
* the sample's audio data
|
|
21
|
-
* @type {Float32Array}
|
|
22
|
-
*/
|
|
23
|
-
sampleData;
|
|
24
|
-
/**
|
|
25
|
-
* Current playback step (rate)
|
|
26
|
-
* @type {number}
|
|
27
|
-
*/
|
|
28
|
-
playbackStep = 0;
|
|
29
|
-
/**
|
|
30
|
-
* Current position in the sample
|
|
31
|
-
* @type {number}
|
|
32
|
-
*/
|
|
33
|
-
cursor = 0;
|
|
34
|
-
/**
|
|
35
|
-
* MIDI root key of the sample
|
|
36
|
-
* @type {number}
|
|
37
|
-
*/
|
|
38
|
-
rootKey = 0;
|
|
39
|
-
/**
|
|
40
|
-
* Start position of the loop
|
|
41
|
-
* @type {number}
|
|
42
|
-
*/
|
|
43
|
-
loopStart = 0;
|
|
44
|
-
/**
|
|
45
|
-
* End position of the loop
|
|
46
|
-
* @type {number}
|
|
47
|
-
*/
|
|
48
|
-
loopEnd = 0;
|
|
49
|
-
/**
|
|
50
|
-
* End position of the sample
|
|
51
|
-
* @type {number}
|
|
52
|
-
*/
|
|
53
|
-
end = 0;
|
|
54
|
-
/**
|
|
55
|
-
* Looping mode of the sample:
|
|
56
|
-
* 0 - no loop
|
|
57
|
-
* 1 - loop
|
|
58
|
-
* 2 - UNOFFICIAL: polyphone 2.4 added start on release
|
|
59
|
-
* 3 - loop then play when released
|
|
60
|
-
* @type {0|1|2|3}
|
|
61
|
-
*/
|
|
62
|
-
loopingMode = 0;
|
|
63
|
-
/**
|
|
64
|
-
* Indicates if the sample is currently looping
|
|
65
|
-
* @type {boolean}
|
|
66
|
-
*/
|
|
67
|
-
isLooping = false;
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* @param data {Float32Array}
|
|
71
|
-
* @param playbackStep {number} the playback step, a single increment
|
|
72
|
-
* @param cursorStart {number} the sample id which starts the playback
|
|
73
|
-
* @param rootKey {number} MIDI root key
|
|
74
|
-
* @param loopStart {number} loop start index
|
|
75
|
-
* @param loopEnd {number} loop end index
|
|
76
|
-
* @param endIndex {number} sample end index (for end offset)
|
|
77
|
-
* @param loopingMode {number} sample looping mode
|
|
78
|
-
*/
|
|
79
|
-
constructor(
|
|
80
|
-
data,
|
|
81
|
-
playbackStep,
|
|
82
|
-
cursorStart,
|
|
83
|
-
rootKey,
|
|
84
|
-
loopStart,
|
|
85
|
-
loopEnd,
|
|
86
|
-
endIndex,
|
|
87
|
-
loopingMode
|
|
88
|
-
)
|
|
89
|
-
{
|
|
90
|
-
this.sampleData = data;
|
|
91
|
-
this.playbackStep = playbackStep;
|
|
92
|
-
this.cursor = cursorStart;
|
|
93
|
-
this.rootKey = rootKey;
|
|
94
|
-
this.loopStart = loopStart;
|
|
95
|
-
this.loopEnd = loopEnd;
|
|
96
|
-
this.end = endIndex;
|
|
97
|
-
this.loopingMode = loopingMode;
|
|
98
|
-
this.isLooping = this.loopingMode === 1 || this.loopingMode === 3;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Voice represents a single instance of the
|
|
105
|
-
* SoundFont2 synthesis model.
|
|
106
|
-
* That is:
|
|
107
|
-
* A wavetable oscillator (sample)
|
|
108
|
-
* A volume envelope (volumeEnvelope)
|
|
109
|
-
* A modulation envelope (modulationEnvelope)
|
|
110
|
-
* Generators (generators and modulatedGenerators)
|
|
111
|
-
* Modulators (modulators)
|
|
112
|
-
* And MIDI params such as channel, MIDI note, velocity
|
|
113
|
-
*/
|
|
114
|
-
class Voice
|
|
115
|
-
{
|
|
116
|
-
/**
|
|
117
|
-
* The sample of the voice.
|
|
118
|
-
* @type {AudioSample}
|
|
119
|
-
*/
|
|
120
|
-
sample;
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Lowpass filter applied to the voice.
|
|
124
|
-
* @type {LowpassFilter}
|
|
125
|
-
*/
|
|
126
|
-
filter;
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Linear gain of the voice. Used with Key Modifiers.
|
|
130
|
-
* @type {number}
|
|
131
|
-
*/
|
|
132
|
-
gain = 1;
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* The unmodulated (copied to) generators of the voice.
|
|
136
|
-
* @type {Int16Array}
|
|
137
|
-
*/
|
|
138
|
-
generators;
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* The voice's modulators.
|
|
142
|
-
* @type {Modulator[]}
|
|
143
|
-
*/
|
|
144
|
-
modulators = [];
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Resonance offset, it is affected by the default resonant modulator
|
|
148
|
-
* @type {number}
|
|
149
|
-
*/
|
|
150
|
-
resonanceOffset = 0;
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* The generators in real-time, affected by modulators.
|
|
154
|
-
* This is used during rendering.
|
|
155
|
-
* @type {Int16Array}
|
|
156
|
-
*/
|
|
157
|
-
modulatedGenerators;
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Indicates if the voice is finished.
|
|
161
|
-
* @type {boolean}
|
|
162
|
-
*/
|
|
163
|
-
finished = false;
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Indicates if the voice is in the release phase.
|
|
167
|
-
* @type {boolean}
|
|
168
|
-
*/
|
|
169
|
-
isInRelease = false;
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Velocity of the note.
|
|
173
|
-
* @type {number}
|
|
174
|
-
*/
|
|
175
|
-
velocity = 0;
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* MIDI note number.
|
|
179
|
-
* @type {number}
|
|
180
|
-
*/
|
|
181
|
-
midiNote = 0;
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* The pressure of the voice
|
|
185
|
-
* @type {number}
|
|
186
|
-
*/
|
|
187
|
-
pressure = 0;
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Target key for the note.
|
|
191
|
-
* @type {number}
|
|
192
|
-
*/
|
|
193
|
-
targetKey = 0;
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Modulation envelope.
|
|
197
|
-
* @type {ModulationEnvelope}
|
|
198
|
-
*/
|
|
199
|
-
modulationEnvelope = new ModulationEnvelope();
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Volume envelope.
|
|
203
|
-
* @type {VolumeEnvelope}
|
|
204
|
-
*/
|
|
205
|
-
volumeEnvelope;
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Start time of the voice, absolute.
|
|
209
|
-
* @type {number}
|
|
210
|
-
*/
|
|
211
|
-
startTime = 0;
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Start time of the release phase, absolute.
|
|
215
|
-
* @type {number}
|
|
216
|
-
*/
|
|
217
|
-
releaseStartTime = Infinity;
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Current tuning in cents.
|
|
221
|
-
* @type {number}
|
|
222
|
-
*/
|
|
223
|
-
currentTuningCents = 0;
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Current calculated tuning. (as in ratio)
|
|
227
|
-
* @type {number}
|
|
228
|
-
*/
|
|
229
|
-
currentTuningCalculated = 1;
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* From -500 to 500.
|
|
233
|
-
* @param {number}
|
|
234
|
-
*/
|
|
235
|
-
currentPan = 0;
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* If MIDI Tuning Standard is already applied (at note-on time),
|
|
239
|
-
* this will be used to take the values at real-time tuning as "midiNote"
|
|
240
|
-
* property contains the tuned number.
|
|
241
|
-
* see #29 comment by @paulikaro
|
|
242
|
-
* @type {number}
|
|
243
|
-
*/
|
|
244
|
-
realKey;
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* @type {number} Initial key to glide from, MIDI Note number. If -1, the portamento is OFF.
|
|
248
|
-
*/
|
|
249
|
-
portamentoFromKey = -1;
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Duration of the linear glide, in seconds.
|
|
253
|
-
* @type {number}
|
|
254
|
-
*/
|
|
255
|
-
portamentoDuration = 0;
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* From -500 to 500, where zero means disabled (use the channel pan). Used for random pan.
|
|
259
|
-
* @type {number}
|
|
260
|
-
*/
|
|
261
|
-
overridePan = 0;
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Exclusive class number for hi-hats etc.
|
|
265
|
-
* @type {number}
|
|
266
|
-
*/
|
|
267
|
-
exclusiveClass = 0;
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Creates a Voice
|
|
271
|
-
* @param sampleRate {number}
|
|
272
|
-
* @param audioSample {AudioSample}
|
|
273
|
-
* @param midiNote {number}
|
|
274
|
-
* @param velocity {number}
|
|
275
|
-
* @param currentTime {number}
|
|
276
|
-
* @param targetKey {number}
|
|
277
|
-
* @param realKey {number}
|
|
278
|
-
* @param generators {Int16Array}
|
|
279
|
-
* @param modulators {Modulator[]}
|
|
280
|
-
*/
|
|
281
|
-
constructor(
|
|
282
|
-
sampleRate,
|
|
283
|
-
audioSample,
|
|
284
|
-
midiNote,
|
|
285
|
-
velocity,
|
|
286
|
-
currentTime,
|
|
287
|
-
targetKey,
|
|
288
|
-
realKey,
|
|
289
|
-
generators,
|
|
290
|
-
modulators
|
|
291
|
-
)
|
|
292
|
-
{
|
|
293
|
-
this.sample = audioSample;
|
|
294
|
-
this.generators = generators;
|
|
295
|
-
this.exclusiveClass = this.generators[generatorTypes.exclusiveClass];
|
|
296
|
-
this.modulatedGenerators = new Int16Array(generators);
|
|
297
|
-
this.modulators = modulators;
|
|
298
|
-
this.filter = new LowpassFilter(sampleRate);
|
|
299
|
-
this.velocity = velocity;
|
|
300
|
-
this.midiNote = midiNote;
|
|
301
|
-
this.startTime = currentTime;
|
|
302
|
-
this.targetKey = targetKey;
|
|
303
|
-
this.realKey = realKey;
|
|
304
|
-
this.volumeEnvelope = new VolumeEnvelope(sampleRate, generators[generatorTypes.sustainVolEnv]);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* copies the voice
|
|
309
|
-
* @param voice {Voice}
|
|
310
|
-
* @param currentTime {number}
|
|
311
|
-
* @param realKey {number}
|
|
312
|
-
* @returns Voice
|
|
313
|
-
*/
|
|
314
|
-
static copy(voice, currentTime, realKey)
|
|
315
|
-
{
|
|
316
|
-
const sampleToCopy = voice.sample;
|
|
317
|
-
const sample = new AudioSample(
|
|
318
|
-
sampleToCopy.sampleData,
|
|
319
|
-
sampleToCopy.playbackStep,
|
|
320
|
-
sampleToCopy.cursor,
|
|
321
|
-
sampleToCopy.rootKey,
|
|
322
|
-
sampleToCopy.loopStart,
|
|
323
|
-
sampleToCopy.loopEnd,
|
|
324
|
-
sampleToCopy.end,
|
|
325
|
-
sampleToCopy.loopingMode
|
|
326
|
-
);
|
|
327
|
-
return new Voice(
|
|
328
|
-
voice.volumeEnvelope.sampleRate,
|
|
329
|
-
sample,
|
|
330
|
-
voice.midiNote,
|
|
331
|
-
voice.velocity,
|
|
332
|
-
currentTime,
|
|
333
|
-
voice.targetKey,
|
|
334
|
-
realKey,
|
|
335
|
-
new Int16Array(voice.generators),
|
|
336
|
-
voice.modulators.map(m => Modulator.copy(m))
|
|
337
|
-
);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Releases the voice as exclusiveClass
|
|
342
|
-
* @param currentTime {number}
|
|
343
|
-
*/
|
|
344
|
-
exclusiveRelease(currentTime)
|
|
345
|
-
{
|
|
346
|
-
this.release(currentTime, MIN_EXCLUSIVE_LENGTH);
|
|
347
|
-
this.modulatedGenerators[generatorTypes.releaseVolEnv] = EXCLUSIVE_CUTOFF_TIME; // make the release nearly instant
|
|
348
|
-
this.modulatedGenerators[generatorTypes.releaseModEnv] = EXCLUSIVE_MOD_CUTOFF_TIME;
|
|
349
|
-
VolumeEnvelope.recalculate(this);
|
|
350
|
-
ModulationEnvelope.recalculate(this);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
/**
|
|
354
|
-
* Stops the voice
|
|
355
|
-
* @param currentTime {number}
|
|
356
|
-
* @param minNoteLength {number} minimum note length in seconds
|
|
357
|
-
*/
|
|
358
|
-
release(currentTime, minNoteLength = MIN_NOTE_LENGTH)
|
|
359
|
-
{
|
|
360
|
-
this.releaseStartTime = currentTime;
|
|
361
|
-
// check if the note is shorter than the min note time, if so, extend it
|
|
362
|
-
if (this.releaseStartTime - this.startTime < minNoteLength)
|
|
363
|
-
{
|
|
364
|
-
this.releaseStartTime = this.startTime + minNoteLength;
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* @param preset {BasicPreset} the preset to get voices for
|
|
371
|
-
* @param bank {number} the bank to cache the voices in
|
|
372
|
-
* @param program {number} program to cache the voices in
|
|
373
|
-
* @param midiNote {number} the MIDI note to use
|
|
374
|
-
* @param velocity {number} the velocity to use
|
|
375
|
-
* @param realKey {number} the real MIDI note if the "midiNote" was changed by MIDI Tuning Standard
|
|
376
|
-
* @this {SpessaSynthProcessor}
|
|
377
|
-
* @returns {Voice[]} output is an array of Voices
|
|
378
|
-
*/
|
|
379
|
-
export function getVoicesForPreset(preset, bank, program, midiNote, velocity, realKey)
|
|
380
|
-
{
|
|
381
|
-
/**
|
|
382
|
-
* @type {Voice[]}
|
|
383
|
-
*/
|
|
384
|
-
const voices = preset.getSamplesAndGenerators(midiNote, velocity)
|
|
385
|
-
.reduce((voices, sampleAndGenerators) =>
|
|
386
|
-
{
|
|
387
|
-
if (sampleAndGenerators.sample.getAudioData() === undefined)
|
|
388
|
-
{
|
|
389
|
-
SpessaSynthWarn(`Discarding invalid sample: ${sampleAndGenerators.sample.sampleName}`);
|
|
390
|
-
return voices;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// create the generator list
|
|
394
|
-
const generators = new Int16Array(GENERATORS_AMOUNT);
|
|
395
|
-
// apply and sum the gens
|
|
396
|
-
for (let i = 0; i < 60; i++)
|
|
397
|
-
{
|
|
398
|
-
generators[i] = addAndClampGenerator(
|
|
399
|
-
i,
|
|
400
|
-
sampleAndGenerators.presetGenerators,
|
|
401
|
-
sampleAndGenerators.instrumentGenerators
|
|
402
|
-
);
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// EMU initial attenuation correction, multiply initial attenuation by 0.4!
|
|
406
|
-
// all EMU sound cards have this quirk, and all sf2 editors and players emulate it too
|
|
407
|
-
generators[generatorTypes.initialAttenuation] = Math.floor(generators[generatorTypes.initialAttenuation] * 0.4);
|
|
408
|
-
|
|
409
|
-
// key override
|
|
410
|
-
let rootKey = sampleAndGenerators.sample.samplePitch;
|
|
411
|
-
if (generators[generatorTypes.overridingRootKey] > -1)
|
|
412
|
-
{
|
|
413
|
-
rootKey = generators[generatorTypes.overridingRootKey];
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
let targetKey = midiNote;
|
|
417
|
-
if (generators[generatorTypes.keyNum] > -1)
|
|
418
|
-
{
|
|
419
|
-
targetKey = generators[generatorTypes.keyNum];
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// determine looping mode now. if the loop is too small, disable
|
|
423
|
-
let loopStart = sampleAndGenerators.sample.sampleLoopStartIndex;
|
|
424
|
-
let loopEnd = sampleAndGenerators.sample.sampleLoopEndIndex;
|
|
425
|
-
let loopingMode = generators[generatorTypes.sampleModes];
|
|
426
|
-
/**
|
|
427
|
-
* create the sample
|
|
428
|
-
* offsets are calculated at note on time (to allow for modulation of them)
|
|
429
|
-
* @type {AudioSample}
|
|
430
|
-
*/
|
|
431
|
-
const audioSample = new AudioSample(
|
|
432
|
-
sampleAndGenerators.sample.sampleData,
|
|
433
|
-
(sampleAndGenerators.sample.sampleRate / this.sampleRate) * Math.pow(
|
|
434
|
-
2,
|
|
435
|
-
sampleAndGenerators.sample.samplePitchCorrection / 1200
|
|
436
|
-
), // cent tuning
|
|
437
|
-
0,
|
|
438
|
-
rootKey,
|
|
439
|
-
loopStart,
|
|
440
|
-
loopEnd,
|
|
441
|
-
Math.floor(sampleAndGenerators.sample.sampleData.length) - 1,
|
|
442
|
-
loopingMode
|
|
443
|
-
);
|
|
444
|
-
// velocity override
|
|
445
|
-
if (generators[generatorTypes.velocity] > -1)
|
|
446
|
-
{
|
|
447
|
-
velocity = generators[generatorTypes.velocity];
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// uncomment to print debug info
|
|
451
|
-
// SpessaSynthTable([{
|
|
452
|
-
// Sample: sampleAndGenerators.sample.sampleName,
|
|
453
|
-
// Generators: generators,
|
|
454
|
-
// Modulators: sampleAndGenerators.modulators.map(m => Modulator.debugString(m)),
|
|
455
|
-
// Velocity: velocity,
|
|
456
|
-
// TargetKey: targetKey,
|
|
457
|
-
// MidiNote: midiNote,
|
|
458
|
-
// AudioSample: audioSample
|
|
459
|
-
// }]);
|
|
460
|
-
voices.push(
|
|
461
|
-
new Voice(
|
|
462
|
-
this.sampleRate,
|
|
463
|
-
audioSample,
|
|
464
|
-
midiNote,
|
|
465
|
-
velocity,
|
|
466
|
-
this.currentSynthTime,
|
|
467
|
-
targetKey,
|
|
468
|
-
realKey,
|
|
469
|
-
generators,
|
|
470
|
-
sampleAndGenerators.modulators.map(m => Modulator.copy(m))
|
|
471
|
-
)
|
|
472
|
-
);
|
|
473
|
-
return voices;
|
|
474
|
-
}, []);
|
|
475
|
-
// cache the voice
|
|
476
|
-
this.setCachedVoice(bank, program, midiNote, velocity, voices);
|
|
477
|
-
return voices.map(v =>
|
|
478
|
-
Voice.copy(v, this.currentSynthTime, realKey));
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* @param channel {number} a hint for the processor to recalculate sample cursors when sample dumping
|
|
483
|
-
* @param midiNote {number} the MIDI note to use
|
|
484
|
-
* @param velocity {number} the velocity to use
|
|
485
|
-
* @param realKey {number} the real MIDI note if the "midiNote" was changed by MIDI Tuning Standard
|
|
486
|
-
* @this {SpessaSynthProcessor}
|
|
487
|
-
* @returns {Voice[]} output is an array of Voices
|
|
488
|
-
*/
|
|
489
|
-
export function getVoices(channel, midiNote, velocity, realKey)
|
|
490
|
-
{
|
|
491
|
-
const channelObject = this.midiAudioChannels[channel];
|
|
492
|
-
|
|
493
|
-
// override patch
|
|
494
|
-
const overridePatch = this.keyModifierManager.hasOverridePatch(channel, midiNote);
|
|
495
|
-
|
|
496
|
-
let bank = channelObject.getBankSelect();
|
|
497
|
-
|
|
498
|
-
let preset = channelObject.preset;
|
|
499
|
-
if (!preset)
|
|
500
|
-
{
|
|
501
|
-
SpessaSynthWarn(`No preset for channel ${channel}!`);
|
|
502
|
-
return [];
|
|
503
|
-
}
|
|
504
|
-
let program = preset.program;
|
|
505
|
-
if (overridePatch)
|
|
506
|
-
{
|
|
507
|
-
const override = this.keyModifierManager.getPatch(channel, midiNote);
|
|
508
|
-
bank = override.bank;
|
|
509
|
-
program = override.program;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
const cached = this.getCachedVoice(bank, program, midiNote, velocity);
|
|
513
|
-
// if cached, return it!
|
|
514
|
-
if (cached !== undefined)
|
|
515
|
-
{
|
|
516
|
-
return cached.map(v => Voice.copy(v, this.currentSynthTime, realKey));
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
// not cached...
|
|
520
|
-
if (overridePatch)
|
|
521
|
-
{
|
|
522
|
-
preset = this.getPreset(bank, program);
|
|
523
|
-
}
|
|
524
|
-
return this.getVoicesForPreset(preset, bank, program, midiNote, velocity, realKey);
|
|
525
|
-
}
|