spessasynth_lib 3.25.23 → 3.26.1
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/external_midi/midi_handler.js +14 -14
- package/external_midi/web_midi_link.js +3 -3
- package/index.js +33 -33
- package/package.json +19 -6
- package/{midi → sequencer}/midi_data.js +1 -1
- package/sequencer/{worklet_wrapper/sequencer.js → sequencer.js} +13 -12
- package/sequencer/{worklet_wrapper/sequencer_message.js → sequencer_message.js} +1 -1
- package/synthetizer/README.md +29 -1
- package/synthetizer/audio_effects/reverb_as_binary.js +2 -2
- package/synthetizer/{worklet_wrapper/key_modifier_manager.js → key_modifier_manager.js} +11 -2
- package/synthetizer/{worklet_wrapper/synth_event_handler.js → synth_event_handler.js} +16 -13
- package/synthetizer/{worklet_wrapper/synth_soundfont_manager.js → synth_soundfont_manager.js} +8 -5
- package/synthetizer/{worklet_wrapper/synthetizer.js → synthetizer.js} +29 -23
- package/synthetizer/{audio_engine/message_protocol/worklet_message.js → worklet_message.js} +15 -2
- package/synthetizer/{worklet_wrapper/worklet_processor.js → worklet_processor.js} +214 -31
- package/synthetizer/worklet_processor.min.js +13 -12
- package/synthetizer/worklet_processor.min.js.map +7 -0
- package/synthetizer/{worklet_wrapper/worklet_url.js → worklet_url.js} +2 -0
- package/utils/buffer_to_wav.js +7 -165
- package/utils/other.js +3 -84
- 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_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/sequencer_engine/events.js +0 -104
- 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 -335
- package/sequencer/sequencer_engine/song_control.js +0 -229
- 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/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.js +0 -228
- 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 -175
- 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 -776
- 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/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/utils/README.md +0 -5
- 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/indexed_array.js +0 -52
- package/utils/loggin.js +0 -79
- package/utils/sysex_detector.js +0 -58
- package/utils/xg_hacks.js +0 -193
- /package/sequencer/{worklet_wrapper/default_sequencer_options.js → default_sequencer_options.js} +0 -0
- /package/synthetizer/{worklet_wrapper/sfman_message.js → sfman_message.js} +0 -0
|
@@ -1,263 +0,0 @@
|
|
|
1
|
-
import { readLittleEndian } from "../../utils/byte_functions/little_endian.js";
|
|
2
|
-
import { IndexedByteArray } from "../../utils/indexed_array.js";
|
|
3
|
-
import { RiffChunk } from "../basic_soundfont/riff_chunk.js";
|
|
4
|
-
import { BasicInstrumentZone, BasicPresetZone } from "../basic_soundfont/basic_zones.js";
|
|
5
|
-
import { Generator, generatorTypes } from "../basic_soundfont/generator.js";
|
|
6
|
-
import { Modulator } from "../basic_soundfont/modulator.js";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* zones.js
|
|
10
|
-
* purpose: reads instrumend and preset zones from soundfont and gets their respective samples and generators and modulators
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
export class InstrumentZone extends BasicInstrumentZone
|
|
14
|
-
{
|
|
15
|
-
/**
|
|
16
|
-
* Creates a zone (instrument)
|
|
17
|
-
* @param dataArray {IndexedByteArray}
|
|
18
|
-
*/
|
|
19
|
-
constructor(dataArray)
|
|
20
|
-
{
|
|
21
|
-
super();
|
|
22
|
-
this.generatorZoneStartIndex = readLittleEndian(dataArray, 2);
|
|
23
|
-
this.modulatorZoneStartIndex = readLittleEndian(dataArray, 2);
|
|
24
|
-
this.modulatorZoneSize = 0;
|
|
25
|
-
this.generatorZoneSize = 0;
|
|
26
|
-
this.isGlobal = true;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
setZoneSize(modulatorZoneSize, generatorZoneSize)
|
|
30
|
-
{
|
|
31
|
-
this.modulatorZoneSize = modulatorZoneSize;
|
|
32
|
-
this.generatorZoneSize = generatorZoneSize;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* grab the generators
|
|
37
|
-
* @param generators {Generator[]}
|
|
38
|
-
*/
|
|
39
|
-
getGenerators(generators)
|
|
40
|
-
{
|
|
41
|
-
for (let i = this.generatorZoneStartIndex; i < this.generatorZoneStartIndex + this.generatorZoneSize; i++)
|
|
42
|
-
{
|
|
43
|
-
this.generators.push(generators[i]);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* grab the modulators
|
|
49
|
-
* @param modulators {Modulator[]}
|
|
50
|
-
*/
|
|
51
|
-
getModulators(modulators)
|
|
52
|
-
{
|
|
53
|
-
for (let i = this.modulatorZoneStartIndex; i < this.modulatorZoneStartIndex + this.modulatorZoneSize; i++)
|
|
54
|
-
{
|
|
55
|
-
this.modulators.push(modulators[i]);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Loads the zone's sample
|
|
61
|
-
* @param samples {BasicSample[]}
|
|
62
|
-
*/
|
|
63
|
-
getSample(samples)
|
|
64
|
-
{
|
|
65
|
-
let sampleID = this.generators.find(g => g.generatorType === generatorTypes.sampleID);
|
|
66
|
-
if (sampleID)
|
|
67
|
-
{
|
|
68
|
-
this.sample = samples[sampleID.generatorValue];
|
|
69
|
-
this.isGlobal = false;
|
|
70
|
-
this.sample.useCount++;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Reads the keyRange of the zone
|
|
76
|
-
*/
|
|
77
|
-
getKeyRange()
|
|
78
|
-
{
|
|
79
|
-
let range = this.generators.find(g => g.generatorType === generatorTypes.keyRange);
|
|
80
|
-
if (range)
|
|
81
|
-
{
|
|
82
|
-
this.keyRange.min = range.generatorValue & 0x7F;
|
|
83
|
-
this.keyRange.max = (range.generatorValue >> 8) & 0x7F;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* reads the velolicty range of the zone
|
|
89
|
-
*/
|
|
90
|
-
getVelRange()
|
|
91
|
-
{
|
|
92
|
-
let range = this.generators.find(g => g.generatorType === generatorTypes.velRange);
|
|
93
|
-
if (range)
|
|
94
|
-
{
|
|
95
|
-
this.velRange.min = range.generatorValue & 0x7F;
|
|
96
|
-
this.velRange.max = (range.generatorValue >> 8) & 0x7F;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Reads the given instrument zone read
|
|
103
|
-
* @param zonesChunk {RiffChunk}
|
|
104
|
-
* @param instrumentGenerators {Generator[]}
|
|
105
|
-
* @param instrumentModulators {Modulator[]}
|
|
106
|
-
* @param instrumentSamples {BasicSample[]}
|
|
107
|
-
* @returns {InstrumentZone[]}
|
|
108
|
-
*/
|
|
109
|
-
export function readInstrumentZones(zonesChunk, instrumentGenerators, instrumentModulators, instrumentSamples)
|
|
110
|
-
{
|
|
111
|
-
/**
|
|
112
|
-
* @type {InstrumentZone[]}
|
|
113
|
-
*/
|
|
114
|
-
let zones = [];
|
|
115
|
-
while (zonesChunk.chunkData.length > zonesChunk.chunkData.currentIndex)
|
|
116
|
-
{
|
|
117
|
-
let zone = new InstrumentZone(zonesChunk.chunkData);
|
|
118
|
-
if (zones.length > 0)
|
|
119
|
-
{
|
|
120
|
-
let modulatorZoneSize = zone.modulatorZoneStartIndex - zones[zones.length - 1].modulatorZoneStartIndex;
|
|
121
|
-
let generatorZoneSize = zone.generatorZoneStartIndex - zones[zones.length - 1].generatorZoneStartIndex;
|
|
122
|
-
zones[zones.length - 1].setZoneSize(modulatorZoneSize, generatorZoneSize);
|
|
123
|
-
zones[zones.length - 1].getGenerators(instrumentGenerators);
|
|
124
|
-
zones[zones.length - 1].getModulators(instrumentModulators);
|
|
125
|
-
zones[zones.length - 1].getSample(instrumentSamples);
|
|
126
|
-
zones[zones.length - 1].getKeyRange();
|
|
127
|
-
zones[zones.length - 1].getVelRange();
|
|
128
|
-
}
|
|
129
|
-
zones.push(zone);
|
|
130
|
-
}
|
|
131
|
-
if (zones.length > 1)
|
|
132
|
-
{
|
|
133
|
-
// remove terminal
|
|
134
|
-
zones.pop();
|
|
135
|
-
}
|
|
136
|
-
return zones;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export class PresetZone extends BasicPresetZone
|
|
140
|
-
{
|
|
141
|
-
/**
|
|
142
|
-
* Creates a zone (preset)
|
|
143
|
-
* @param dataArray {IndexedByteArray}
|
|
144
|
-
*/
|
|
145
|
-
constructor(dataArray)
|
|
146
|
-
{
|
|
147
|
-
super();
|
|
148
|
-
this.generatorZoneStartIndex = readLittleEndian(dataArray, 2);
|
|
149
|
-
this.modulatorZoneStartIndex = readLittleEndian(dataArray, 2);
|
|
150
|
-
this.modulatorZoneSize = 0;
|
|
151
|
-
this.generatorZoneSize = 0;
|
|
152
|
-
this.isGlobal = true;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
setZoneSize(modulatorZoneSize, generatorZoneSize)
|
|
156
|
-
{
|
|
157
|
-
this.modulatorZoneSize = modulatorZoneSize;
|
|
158
|
-
this.generatorZoneSize = generatorZoneSize;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* grab the generators
|
|
163
|
-
* @param generators {Generator[]}
|
|
164
|
-
*/
|
|
165
|
-
getGenerators(generators)
|
|
166
|
-
{
|
|
167
|
-
for (let i = this.generatorZoneStartIndex; i < this.generatorZoneStartIndex + this.generatorZoneSize; i++)
|
|
168
|
-
{
|
|
169
|
-
this.generators.push(generators[i]);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* grab the modulators
|
|
175
|
-
* @param modulators {Modulator[]}
|
|
176
|
-
*/
|
|
177
|
-
getModulators(modulators)
|
|
178
|
-
{
|
|
179
|
-
for (let i = this.modulatorZoneStartIndex; i < this.modulatorZoneStartIndex + this.modulatorZoneSize; i++)
|
|
180
|
-
{
|
|
181
|
-
this.modulators.push(modulators[i]);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* grab the instrument
|
|
187
|
-
* @param instruments {BasicInstrument[]}
|
|
188
|
-
*/
|
|
189
|
-
getInstrument(instruments)
|
|
190
|
-
{
|
|
191
|
-
let instrumentID = this.generators.find(g => g.generatorType === generatorTypes.instrument);
|
|
192
|
-
if (instrumentID)
|
|
193
|
-
{
|
|
194
|
-
this.instrument = instruments[instrumentID.generatorValue];
|
|
195
|
-
this.instrument.addUseCount();
|
|
196
|
-
this.isGlobal = false;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Reads the keyRange of the zone
|
|
202
|
-
*/
|
|
203
|
-
getKeyRange()
|
|
204
|
-
{
|
|
205
|
-
let range = this.generators.find(g => g.generatorType === generatorTypes.keyRange);
|
|
206
|
-
if (range)
|
|
207
|
-
{
|
|
208
|
-
this.keyRange.min = range.generatorValue & 0x7F;
|
|
209
|
-
this.keyRange.max = (range.generatorValue >> 8) & 0x7F;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* reads the velolicty range of the zone
|
|
215
|
-
*/
|
|
216
|
-
getVelRange()
|
|
217
|
-
{
|
|
218
|
-
let range = this.generators.find(g => g.generatorType === generatorTypes.velRange);
|
|
219
|
-
if (range)
|
|
220
|
-
{
|
|
221
|
-
this.velRange.min = range.generatorValue & 0x7F;
|
|
222
|
-
this.velRange.max = (range.generatorValue >> 8) & 0x7F;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Reads the given preset zone read
|
|
229
|
-
* @param zonesChunk {RiffChunk}
|
|
230
|
-
* @param presetGenerators {Generator[]}
|
|
231
|
-
* @param instruments {BasicInstrument[]}
|
|
232
|
-
* @param presetModulators {Modulator[]}
|
|
233
|
-
* @returns {PresetZone[]}
|
|
234
|
-
*/
|
|
235
|
-
export function readPresetZones(zonesChunk, presetGenerators, presetModulators, instruments)
|
|
236
|
-
{
|
|
237
|
-
/**
|
|
238
|
-
* @type {PresetZone[]}
|
|
239
|
-
*/
|
|
240
|
-
let zones = [];
|
|
241
|
-
while (zonesChunk.chunkData.length > zonesChunk.chunkData.currentIndex)
|
|
242
|
-
{
|
|
243
|
-
let zone = new PresetZone(zonesChunk.chunkData);
|
|
244
|
-
if (zones.length > 0)
|
|
245
|
-
{
|
|
246
|
-
let modulatorZoneSize = zone.modulatorZoneStartIndex - zones[zones.length - 1].modulatorZoneStartIndex;
|
|
247
|
-
let generatorZoneSize = zone.generatorZoneStartIndex - zones[zones.length - 1].generatorZoneStartIndex;
|
|
248
|
-
zones[zones.length - 1].setZoneSize(modulatorZoneSize, generatorZoneSize);
|
|
249
|
-
zones[zones.length - 1].getGenerators(presetGenerators);
|
|
250
|
-
zones[zones.length - 1].getModulators(presetModulators);
|
|
251
|
-
zones[zones.length - 1].getInstrument(instruments);
|
|
252
|
-
zones[zones.length - 1].getKeyRange();
|
|
253
|
-
zones[zones.length - 1].getVelRange();
|
|
254
|
-
}
|
|
255
|
-
zones.push(zone);
|
|
256
|
-
}
|
|
257
|
-
if (zones.length > 1)
|
|
258
|
-
{
|
|
259
|
-
// remove terminal
|
|
260
|
-
zones.pop();
|
|
261
|
-
}
|
|
262
|
-
return zones;
|
|
263
|
-
}
|
|
@@ -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
|
-
};
|