spessasynth_lib 3.25.22 → 3.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -114
- package/index.js +33 -33
- package/package.json +16 -6
- package/external_midi/README.md +0 -4
- package/external_midi/midi_handler.js +0 -130
- package/external_midi/web_midi_link.js +0 -43
- package/externals/fflate/LICENSE +0 -21
- package/externals/fflate/fflate.min.js +0 -1
- package/externals/stbvorbis_sync/@types/stbvorbis_sync.d.ts +0 -12
- package/externals/stbvorbis_sync/LICENSE +0 -202
- package/externals/stbvorbis_sync/NOTICE +0 -6
- package/externals/stbvorbis_sync/stbvorbis_sync.min.js +0 -1
- package/midi/README.md +0 -32
- package/midi/basic_midi.js +0 -565
- package/midi/midi_builder.js +0 -202
- package/midi/midi_data.js +0 -63
- package/midi/midi_loader.js +0 -324
- package/midi/midi_message.js +0 -254
- package/midi/midi_sequence.js +0 -225
- package/midi/midi_tools/get_note_times.js +0 -154
- package/midi/midi_tools/midi_editor.js +0 -611
- package/midi/midi_tools/midi_writer.js +0 -99
- package/midi/midi_tools/rmidi_writer.js +0 -567
- package/midi/midi_tools/used_keys_loaded.js +0 -238
- package/midi/xmf_loader.js +0 -454
- package/sequencer/README.md +0 -32
- package/sequencer/sequencer_engine/events.js +0 -207
- package/sequencer/sequencer_engine/play.js +0 -353
- package/sequencer/sequencer_engine/process_event.js +0 -169
- package/sequencer/sequencer_engine/process_tick.js +0 -106
- package/sequencer/sequencer_engine/sequencer_engine.js +0 -337
- package/sequencer/sequencer_engine/song_control.js +0 -229
- package/sequencer/worklet_wrapper/default_sequencer_options.js +0 -8
- package/sequencer/worklet_wrapper/sequencer.js +0 -807
- package/sequencer/worklet_wrapper/sequencer_message.js +0 -53
- package/soundfont/README.md +0 -13
- package/soundfont/basic_soundfont/basic_instrument.js +0 -77
- package/soundfont/basic_soundfont/basic_preset.js +0 -336
- package/soundfont/basic_soundfont/basic_sample.js +0 -197
- package/soundfont/basic_soundfont/basic_soundfont.js +0 -565
- package/soundfont/basic_soundfont/basic_zone.js +0 -64
- package/soundfont/basic_soundfont/basic_zones.js +0 -43
- package/soundfont/basic_soundfont/generator.js +0 -220
- package/soundfont/basic_soundfont/modulator.js +0 -378
- package/soundfont/basic_soundfont/riff_chunk.js +0 -149
- package/soundfont/basic_soundfont/write_dls/art2.js +0 -173
- package/soundfont/basic_soundfont/write_dls/articulator.js +0 -49
- package/soundfont/basic_soundfont/write_dls/combine_zones.js +0 -400
- package/soundfont/basic_soundfont/write_dls/ins.js +0 -103
- package/soundfont/basic_soundfont/write_dls/lins.js +0 -18
- package/soundfont/basic_soundfont/write_dls/modulator_converter.js +0 -330
- package/soundfont/basic_soundfont/write_dls/rgn2.js +0 -121
- package/soundfont/basic_soundfont/write_dls/wave.js +0 -94
- package/soundfont/basic_soundfont/write_dls/write_dls.js +0 -119
- package/soundfont/basic_soundfont/write_dls/wsmp.js +0 -78
- package/soundfont/basic_soundfont/write_dls/wvpl.js +0 -32
- package/soundfont/basic_soundfont/write_sf2/ibag.js +0 -39
- package/soundfont/basic_soundfont/write_sf2/igen.js +0 -80
- package/soundfont/basic_soundfont/write_sf2/imod.js +0 -46
- package/soundfont/basic_soundfont/write_sf2/inst.js +0 -34
- package/soundfont/basic_soundfont/write_sf2/pbag.js +0 -39
- package/soundfont/basic_soundfont/write_sf2/pgen.js +0 -82
- package/soundfont/basic_soundfont/write_sf2/phdr.js +0 -42
- package/soundfont/basic_soundfont/write_sf2/pmod.js +0 -46
- package/soundfont/basic_soundfont/write_sf2/sdta.js +0 -80
- package/soundfont/basic_soundfont/write_sf2/shdr.js +0 -55
- package/soundfont/basic_soundfont/write_sf2/write.js +0 -222
- package/soundfont/dls/articulator_converter.js +0 -396
- package/soundfont/dls/dls_destinations.js +0 -38
- package/soundfont/dls/dls_preset.js +0 -44
- package/soundfont/dls/dls_sample.js +0 -75
- package/soundfont/dls/dls_soundfont.js +0 -186
- package/soundfont/dls/dls_sources.js +0 -62
- package/soundfont/dls/dls_zone.js +0 -95
- package/soundfont/dls/read_articulation.js +0 -299
- package/soundfont/dls/read_instrument.js +0 -121
- package/soundfont/dls/read_instrument_list.js +0 -17
- package/soundfont/dls/read_lart.js +0 -35
- package/soundfont/dls/read_region.js +0 -152
- package/soundfont/dls/read_samples.js +0 -270
- package/soundfont/load_soundfont.js +0 -21
- package/soundfont/read_sf2/generators.js +0 -46
- package/soundfont/read_sf2/instruments.js +0 -66
- package/soundfont/read_sf2/modulators.js +0 -36
- package/soundfont/read_sf2/presets.js +0 -80
- package/soundfont/read_sf2/samples.js +0 -304
- package/soundfont/read_sf2/soundfont.js +0 -305
- package/soundfont/read_sf2/zones.js +0 -263
- package/synthetizer/README.md +0 -10
- package/synthetizer/audio_effects/effects_config.js +0 -25
- package/synthetizer/audio_effects/fancy_chorus.js +0 -162
- package/synthetizer/audio_effects/rb_compressed.min.js +0 -1
- package/synthetizer/audio_effects/reverb.js +0 -35
- package/synthetizer/audio_effects/reverb_as_binary.js +0 -18
- package/synthetizer/audio_engine/README.md +0 -25
- package/synthetizer/audio_engine/engine_components/compute_modulator.js +0 -266
- package/synthetizer/audio_engine/engine_components/controller_tables.js +0 -88
- package/synthetizer/audio_engine/engine_components/key_modifier_manager.js +0 -149
- package/synthetizer/audio_engine/engine_components/lfo.js +0 -26
- package/synthetizer/audio_engine/engine_components/lowpass_filter.js +0 -282
- package/synthetizer/audio_engine/engine_components/midi_audio_channel.js +0 -471
- package/synthetizer/audio_engine/engine_components/modulation_envelope.js +0 -181
- package/synthetizer/audio_engine/engine_components/modulator_curves.js +0 -89
- package/synthetizer/audio_engine/engine_components/soundfont_manager/sfman_message.js +0 -9
- package/synthetizer/audio_engine/engine_components/soundfont_manager/soundfont_manager.js +0 -254
- package/synthetizer/audio_engine/engine_components/stereo_panner.js +0 -120
- package/synthetizer/audio_engine/engine_components/unit_converter.js +0 -73
- package/synthetizer/audio_engine/engine_components/voice.js +0 -519
- package/synthetizer/audio_engine/engine_components/volume_envelope.js +0 -401
- package/synthetizer/audio_engine/engine_components/wavetable_oscillator.js +0 -263
- package/synthetizer/audio_engine/engine_methods/controller_control/controller_change.js +0 -132
- package/synthetizer/audio_engine/engine_methods/controller_control/master_parameters.js +0 -48
- package/synthetizer/audio_engine/engine_methods/controller_control/reset_controllers.js +0 -241
- package/synthetizer/audio_engine/engine_methods/create_midi_channel.js +0 -27
- package/synthetizer/audio_engine/engine_methods/data_entry/data_entry_coarse.js +0 -253
- package/synthetizer/audio_engine/engine_methods/data_entry/data_entry_fine.js +0 -66
- package/synthetizer/audio_engine/engine_methods/mute_channel.js +0 -17
- package/synthetizer/audio_engine/engine_methods/note_on.js +0 -174
- package/synthetizer/audio_engine/engine_methods/portamento_time.js +0 -92
- package/synthetizer/audio_engine/engine_methods/program_change.js +0 -61
- package/synthetizer/audio_engine/engine_methods/render_voice.js +0 -196
- package/synthetizer/audio_engine/engine_methods/soundfont_management/clear_sound_font.js +0 -30
- package/synthetizer/audio_engine/engine_methods/soundfont_management/get_preset.js +0 -22
- package/synthetizer/audio_engine/engine_methods/soundfont_management/reload_sound_font.js +0 -40
- package/synthetizer/audio_engine/engine_methods/soundfont_management/send_preset_list.js +0 -34
- package/synthetizer/audio_engine/engine_methods/soundfont_management/set_embedded_sound_font.js +0 -21
- package/synthetizer/audio_engine/engine_methods/stopping_notes/kill_note.js +0 -20
- package/synthetizer/audio_engine/engine_methods/stopping_notes/note_off.js +0 -55
- package/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_channels.js +0 -16
- package/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_notes.js +0 -30
- package/synthetizer/audio_engine/engine_methods/stopping_notes/voice_killing.js +0 -63
- package/synthetizer/audio_engine/engine_methods/system_exclusive.js +0 -744
- package/synthetizer/audio_engine/engine_methods/tuning_control/channel_pressure.js +0 -24
- package/synthetizer/audio_engine/engine_methods/tuning_control/pitch_wheel.js +0 -33
- package/synthetizer/audio_engine/engine_methods/tuning_control/poly_pressure.js +0 -31
- package/synthetizer/audio_engine/engine_methods/tuning_control/set_master_tuning.js +0 -15
- package/synthetizer/audio_engine/engine_methods/tuning_control/set_modulation_depth.js +0 -27
- package/synthetizer/audio_engine/engine_methods/tuning_control/set_octave_tuning.js +0 -19
- package/synthetizer/audio_engine/engine_methods/tuning_control/set_tuning.js +0 -27
- package/synthetizer/audio_engine/engine_methods/tuning_control/transpose_all_channels.js +0 -15
- package/synthetizer/audio_engine/engine_methods/tuning_control/transpose_channel.js +0 -34
- package/synthetizer/audio_engine/main_processor.js +0 -765
- package/synthetizer/audio_engine/message_protocol/README.md +0 -13
- package/synthetizer/audio_engine/message_protocol/message_sending.js +0 -22
- package/synthetizer/audio_engine/message_protocol/worklet_message.js +0 -107
- package/synthetizer/audio_engine/snapshot/apply_synthesizer_snapshot.js +0 -14
- package/synthetizer/audio_engine/snapshot/channel_snapshot.js +0 -175
- package/synthetizer/audio_engine/snapshot/synthesizer_snapshot.js +0 -122
- package/synthetizer/synth_constants.js +0 -20
- package/synthetizer/worklet_processor.min.js +0 -21
- package/synthetizer/worklet_wrapper/key_modifier_manager.js +0 -104
- package/synthetizer/worklet_wrapper/synth_event_handler.js +0 -214
- package/synthetizer/worklet_wrapper/synth_soundfont_manager.js +0 -111
- package/synthetizer/worklet_wrapper/synthetizer.js +0 -1027
- package/synthetizer/worklet_wrapper/worklet_processor.js +0 -340
- package/synthetizer/worklet_wrapper/worklet_url.js +0 -16
- package/utils/README.md +0 -5
- package/utils/buffer_to_wav.js +0 -186
- package/utils/byte_functions/big_endian.js +0 -32
- package/utils/byte_functions/little_endian.js +0 -77
- package/utils/byte_functions/string.js +0 -107
- package/utils/byte_functions/variable_length_quantity.js +0 -42
- package/utils/fill_with_defaults.js +0 -21
- package/utils/indexed_array.js +0 -52
- package/utils/loggin.js +0 -79
- package/utils/other.js +0 -92
- package/utils/sysex_detector.js +0 -58
- package/utils/xg_hacks.js +0 -193
|
@@ -1,340 +0,0 @@
|
|
|
1
|
-
import { consoleColors } from "../../utils/other.js";
|
|
2
|
-
import { SpessaSynthInfo, SpessaSynthLogging, SpessaSynthWarn } from "../../utils/loggin.js";
|
|
3
|
-
import { SpessaSynthProcessor } from "../audio_engine/main_processor.js";
|
|
4
|
-
import {
|
|
5
|
-
ALL_CHANNELS_OR_DIFFERENT_ACTION,
|
|
6
|
-
returnMessageType,
|
|
7
|
-
workletMessageType
|
|
8
|
-
} from "../audio_engine/message_protocol/worklet_message.js";
|
|
9
|
-
import { SynthesizerSnapshot } from "../audio_engine/snapshot/synthesizer_snapshot.js";
|
|
10
|
-
import { WORKLET_PROCESSOR_NAME } from "./worklet_url.js";
|
|
11
|
-
import { MIDI_CHANNEL_COUNT } from "../synth_constants.js";
|
|
12
|
-
import { workletKeyModifierMessageType } from "../audio_engine/engine_components/key_modifier_manager.js";
|
|
13
|
-
import { masterParameterType } from "../audio_engine/engine_methods/controller_control/master_parameters.js";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
// a worklet processor wrapper for the synthesizer core
|
|
17
|
-
class WorkletSpessaProcessor extends AudioWorkletProcessor
|
|
18
|
-
{
|
|
19
|
-
/**
|
|
20
|
-
* If the worklet is alive
|
|
21
|
-
* @type {boolean}
|
|
22
|
-
*/
|
|
23
|
-
alive = true;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Instead of 18 stereo outputs, there's one with 32 channels (no effects)
|
|
27
|
-
* @type {boolean}
|
|
28
|
-
*/
|
|
29
|
-
oneOutputMode = false;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Creates a new worklet synthesis system. contains all channels
|
|
33
|
-
* @param options {{
|
|
34
|
-
* processorOptions: {
|
|
35
|
-
* midiChannels: number,
|
|
36
|
-
* soundfont: ArrayBuffer,
|
|
37
|
-
* enableEventSystem: boolean,
|
|
38
|
-
* startRenderingData: StartRenderingDataConfig
|
|
39
|
-
* }}}
|
|
40
|
-
*/
|
|
41
|
-
constructor(options)
|
|
42
|
-
{
|
|
43
|
-
super();
|
|
44
|
-
const opts = options.processorOptions;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
// one output is indicated by setting midiChannels to 1
|
|
48
|
-
this.oneOutputMode = opts.midiChannels === 1;
|
|
49
|
-
|
|
50
|
-
// noinspection JSUnresolvedReference
|
|
51
|
-
/**
|
|
52
|
-
* Initialize the synthesis engine
|
|
53
|
-
* @type {SpessaSynthProcessor}
|
|
54
|
-
*/
|
|
55
|
-
this.synthesizer = new SpessaSynthProcessor(
|
|
56
|
-
MIDI_CHANNEL_COUNT, // midi channel count (16)
|
|
57
|
-
opts.soundfont, // initial sound bank
|
|
58
|
-
sampleRate, // AudioWorkletGlobalScope
|
|
59
|
-
this.postMessageToMainThread.bind(this), // connect to message port
|
|
60
|
-
!this.oneOutputMode, // one output mode disables effects
|
|
61
|
-
opts?.enableEventSystem, // enable message port?
|
|
62
|
-
opts?.startRenderingData, // start rendering?
|
|
63
|
-
currentTime // AudioWorkletGlobalScope, sync with audioContext time
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
// receive messages from the main thread
|
|
67
|
-
this.port.onmessage = e => this.handleMessage(e.data);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* @param data {WorkletReturnMessage}
|
|
72
|
-
*/
|
|
73
|
-
postMessageToMainThread(data)
|
|
74
|
-
{
|
|
75
|
-
this.port.postMessage(data);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* @this {WorkletSpessaProcessor}
|
|
80
|
-
* @param message {WorkletMessage}
|
|
81
|
-
*/
|
|
82
|
-
handleMessage(message)
|
|
83
|
-
{
|
|
84
|
-
const data = message.messageData;
|
|
85
|
-
const channel = message.channelNumber;
|
|
86
|
-
/**
|
|
87
|
-
* @type {MidiAudioChannel}
|
|
88
|
-
*/
|
|
89
|
-
let channelObject;
|
|
90
|
-
if (channel >= 0)
|
|
91
|
-
{
|
|
92
|
-
channelObject = this.synthesizer.midiAudioChannels[channel];
|
|
93
|
-
if (channelObject === undefined)
|
|
94
|
-
{
|
|
95
|
-
SpessaSynthWarn(`Trying to access channel ${channel} which does not exist... ignoring!`);
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
switch (message.messageType)
|
|
100
|
-
{
|
|
101
|
-
case workletMessageType.midiMessage:
|
|
102
|
-
this.synthesizer.processMessage(...data);
|
|
103
|
-
break;
|
|
104
|
-
|
|
105
|
-
case workletMessageType.customcCcChange:
|
|
106
|
-
// custom controller change
|
|
107
|
-
channelObject.setCustomController(data[0], data[1]);
|
|
108
|
-
channelObject.updateChannelTuning();
|
|
109
|
-
break;
|
|
110
|
-
|
|
111
|
-
case workletMessageType.ccReset:
|
|
112
|
-
if (channel === ALL_CHANNELS_OR_DIFFERENT_ACTION)
|
|
113
|
-
{
|
|
114
|
-
this.synthesizer.resetAllControllers();
|
|
115
|
-
}
|
|
116
|
-
else
|
|
117
|
-
{
|
|
118
|
-
channelObject.resetControllers();
|
|
119
|
-
}
|
|
120
|
-
break;
|
|
121
|
-
|
|
122
|
-
case workletMessageType.setChannelVibrato:
|
|
123
|
-
if (channel === ALL_CHANNELS_OR_DIFFERENT_ACTION)
|
|
124
|
-
{
|
|
125
|
-
for (let i = 0; i < this.synthesizer.midiAudioChannels.length; i++)
|
|
126
|
-
{
|
|
127
|
-
const chan = this.synthesizer.midiAudioChannels[i];
|
|
128
|
-
if (data.rate === ALL_CHANNELS_OR_DIFFERENT_ACTION)
|
|
129
|
-
{
|
|
130
|
-
chan.disableAndLockGSNRPN();
|
|
131
|
-
}
|
|
132
|
-
else
|
|
133
|
-
{
|
|
134
|
-
chan.setVibrato(data.depth, data.rate, data.delay);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
else if (data.rate === ALL_CHANNELS_OR_DIFFERENT_ACTION)
|
|
139
|
-
{
|
|
140
|
-
channelObject.disableAndLockGSNRPN();
|
|
141
|
-
}
|
|
142
|
-
else
|
|
143
|
-
{
|
|
144
|
-
channelObject.setVibrato(data.depth, data.rate, data.delay);
|
|
145
|
-
}
|
|
146
|
-
break;
|
|
147
|
-
|
|
148
|
-
case workletMessageType.stopAll:
|
|
149
|
-
if (channel === ALL_CHANNELS_OR_DIFFERENT_ACTION)
|
|
150
|
-
{
|
|
151
|
-
this.synthesizer.stopAllChannels(data === 1);
|
|
152
|
-
}
|
|
153
|
-
else
|
|
154
|
-
{
|
|
155
|
-
channelObject.stopAllNotes(data === 1);
|
|
156
|
-
}
|
|
157
|
-
break;
|
|
158
|
-
|
|
159
|
-
case workletMessageType.killNotes:
|
|
160
|
-
this.synthesizer.voiceKilling(data);
|
|
161
|
-
break;
|
|
162
|
-
|
|
163
|
-
case workletMessageType.muteChannel:
|
|
164
|
-
channelObject.muteChannel(data);
|
|
165
|
-
break;
|
|
166
|
-
|
|
167
|
-
case workletMessageType.addNewChannel:
|
|
168
|
-
this.synthesizer.createWorkletChannel(true);
|
|
169
|
-
break;
|
|
170
|
-
|
|
171
|
-
case workletMessageType.debugMessage:
|
|
172
|
-
this.synthesizer.debugMessage();
|
|
173
|
-
break;
|
|
174
|
-
|
|
175
|
-
case workletMessageType.setMasterParameter:
|
|
176
|
-
/**
|
|
177
|
-
* @type {masterParameterType}
|
|
178
|
-
*/
|
|
179
|
-
const type = data[0];
|
|
180
|
-
const value = data[1];
|
|
181
|
-
this.synthesizer.setMasterParameter(type, value);
|
|
182
|
-
break;
|
|
183
|
-
|
|
184
|
-
case workletMessageType.setDrums:
|
|
185
|
-
channelObject.setDrums(data);
|
|
186
|
-
break;
|
|
187
|
-
|
|
188
|
-
case workletMessageType.transpose:
|
|
189
|
-
if (channel === ALL_CHANNELS_OR_DIFFERENT_ACTION)
|
|
190
|
-
{
|
|
191
|
-
this.synthesizer.transposeAllChannels(data[0], data[1]);
|
|
192
|
-
}
|
|
193
|
-
else
|
|
194
|
-
{
|
|
195
|
-
channelObject.transposeChannel(data[0], data[1]);
|
|
196
|
-
}
|
|
197
|
-
break;
|
|
198
|
-
|
|
199
|
-
case workletMessageType.highPerformanceMode:
|
|
200
|
-
this.synthesizer.highPerformanceMode = data;
|
|
201
|
-
break;
|
|
202
|
-
|
|
203
|
-
case workletMessageType.lockController:
|
|
204
|
-
if (data[0] === ALL_CHANNELS_OR_DIFFERENT_ACTION)
|
|
205
|
-
{
|
|
206
|
-
channelObject.setPresetLock(data[1]);
|
|
207
|
-
}
|
|
208
|
-
else
|
|
209
|
-
{
|
|
210
|
-
channelObject.lockedControllers[data[0]] = data[1];
|
|
211
|
-
}
|
|
212
|
-
break;
|
|
213
|
-
|
|
214
|
-
case workletMessageType.sequencerSpecific:
|
|
215
|
-
this.synthesizer.sequencer.processMessage(data.messageType, data.messageData);
|
|
216
|
-
break;
|
|
217
|
-
|
|
218
|
-
case workletMessageType.soundFontManager:
|
|
219
|
-
try
|
|
220
|
-
{
|
|
221
|
-
this.synthesizer.soundfontManager.handleMessage(data[0], data[1]);
|
|
222
|
-
}
|
|
223
|
-
catch (e)
|
|
224
|
-
{
|
|
225
|
-
this.postMessageToMainThread({
|
|
226
|
-
messageType: returnMessageType.soundfontError,
|
|
227
|
-
messageData: e
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
this.synthesizer.clearSoundFont(true, false);
|
|
231
|
-
break;
|
|
232
|
-
|
|
233
|
-
case workletMessageType.keyModifierManager:
|
|
234
|
-
/**
|
|
235
|
-
* @type {workletKeyModifierMessageType}
|
|
236
|
-
*/
|
|
237
|
-
const keyMessageType = data[0];
|
|
238
|
-
const man = this.synthesizer.keyModifierManager;
|
|
239
|
-
const keyMessageData = data[1];
|
|
240
|
-
switch (keyMessageType)
|
|
241
|
-
{
|
|
242
|
-
default:
|
|
243
|
-
return;
|
|
244
|
-
|
|
245
|
-
case workletKeyModifierMessageType.addMapping:
|
|
246
|
-
man.addMapping(...keyMessageData);
|
|
247
|
-
break;
|
|
248
|
-
|
|
249
|
-
case workletKeyModifierMessageType.clearMappings:
|
|
250
|
-
man.clearMappings();
|
|
251
|
-
break;
|
|
252
|
-
|
|
253
|
-
case workletKeyModifierMessageType.deleteMapping:
|
|
254
|
-
man.deleteMapping(...keyMessageData);
|
|
255
|
-
}
|
|
256
|
-
break;
|
|
257
|
-
|
|
258
|
-
case workletMessageType.requestSynthesizerSnapshot:
|
|
259
|
-
const snapshot = SynthesizerSnapshot.createSynthesizerSnapshot(this.synthesizer);
|
|
260
|
-
this.postMessageToMainThread({
|
|
261
|
-
messageType: returnMessageType.synthesizerSnapshot,
|
|
262
|
-
messageData: snapshot
|
|
263
|
-
});
|
|
264
|
-
break;
|
|
265
|
-
|
|
266
|
-
case workletMessageType.setLogLevel:
|
|
267
|
-
SpessaSynthLogging(data[0], data[1], data[2], data[3]);
|
|
268
|
-
break;
|
|
269
|
-
|
|
270
|
-
case workletMessageType.setEffectsGain:
|
|
271
|
-
this.synthesizer.reverbGain = data[0];
|
|
272
|
-
this.synthesizer.chorusGain = data[1];
|
|
273
|
-
break;
|
|
274
|
-
|
|
275
|
-
case workletMessageType.destroyWorklet:
|
|
276
|
-
this.alive = false;
|
|
277
|
-
this.synthesizer.destroySynthProcessor();
|
|
278
|
-
break;
|
|
279
|
-
|
|
280
|
-
default:
|
|
281
|
-
SpessaSynthWarn("Unrecognized event:", data);
|
|
282
|
-
break;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// noinspection JSUnusedGlobalSymbols
|
|
287
|
-
/**
|
|
288
|
-
* the audio worklet processing logic
|
|
289
|
-
* @param inputs {Float32Array[][]} required by WebAudioAPI
|
|
290
|
-
* @param outputs {Float32Array[][]} the outputs to write to, only the first two channels are populated
|
|
291
|
-
* @returns {boolean} true unless it's not alive
|
|
292
|
-
*/
|
|
293
|
-
process(inputs, outputs)
|
|
294
|
-
{
|
|
295
|
-
if (!this.alive)
|
|
296
|
-
{
|
|
297
|
-
return false;
|
|
298
|
-
}
|
|
299
|
-
if (this.oneOutputMode)
|
|
300
|
-
{
|
|
301
|
-
const out = outputs[0];
|
|
302
|
-
// 1 output with 32 channels.
|
|
303
|
-
// channels are ordered as follows:
|
|
304
|
-
// midiChannel1L, midiChannel1R,
|
|
305
|
-
// midiChannel2L, midiChannel2R
|
|
306
|
-
// and so on
|
|
307
|
-
/**
|
|
308
|
-
* @type {Float32Array[][]}
|
|
309
|
-
*/
|
|
310
|
-
const channelMap = [];
|
|
311
|
-
for (let i = 0; i < 32; i += 2)
|
|
312
|
-
{
|
|
313
|
-
channelMap.push([out[i], out[i + 1]]);
|
|
314
|
-
}
|
|
315
|
-
this.synthesizer.renderAudioSplit(
|
|
316
|
-
[], [], // effects are disabled
|
|
317
|
-
channelMap
|
|
318
|
-
);
|
|
319
|
-
}
|
|
320
|
-
else
|
|
321
|
-
{
|
|
322
|
-
// 18 outputs, each a stereo one
|
|
323
|
-
// 0: reverb
|
|
324
|
-
// 1: chorus
|
|
325
|
-
// 2: channel 1
|
|
326
|
-
// 3: channel 2
|
|
327
|
-
// and so on
|
|
328
|
-
this.synthesizer.renderAudioSplit(
|
|
329
|
-
outputs[0], // reverb
|
|
330
|
-
outputs[1], // chorus
|
|
331
|
-
outputs.slice(2)
|
|
332
|
-
);
|
|
333
|
-
}
|
|
334
|
-
return true;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// noinspection JSUnresolvedReference
|
|
339
|
-
registerProcessor(WORKLET_PROCESSOR_NAME, WorkletSpessaProcessor);
|
|
340
|
-
SpessaSynthInfo("%cProcessor succesfully registered!", consoleColors.recognized);
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The absolute path (from the spessasynth_lib folder) to the worklet module
|
|
3
|
-
* @type {string}
|
|
4
|
-
*/
|
|
5
|
-
export const WORKLET_URL_ABSOLUTE = "synthetizer/worklet_processor.min.js";
|
|
6
|
-
/**
|
|
7
|
-
* @typedef {Object} StartRenderingDataConfig
|
|
8
|
-
* @property {BasicMIDI} parsedMIDI - the MIDI to render
|
|
9
|
-
* @property {SynthesizerSnapshot?} snapshot - the snapshot to apply
|
|
10
|
-
* @property {boolean?} oneOutput - if synth should use one output with 32 channels (2 audio channels for each midi channel).
|
|
11
|
-
* this disabled chorus and reverb.
|
|
12
|
-
* @property {number?} loopCount - the times to loop the song
|
|
13
|
-
* @property {SequencerOptions?} sequencerOptions - the options to pass to the sequencer
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
export const WORKLET_PROCESSOR_NAME = "spessasynth-worklet-processor";
|
package/utils/README.md
DELETED
package/utils/buffer_to_wav.js
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {Object} WaveMetadata
|
|
3
|
-
* @property {string|undefined} title - the song's title
|
|
4
|
-
* @property {string|undefined} artist - the song's artist
|
|
5
|
-
* @property {string|undefined} album - the song's album
|
|
6
|
-
* @property {string|undefined} genre - the song's genre
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { combineArrays, IndexedByteArray } from "./indexed_array.js";
|
|
10
|
-
import { getStringBytes, writeStringAsBytes } from "./byte_functions/string.js";
|
|
11
|
-
import { writeRIFFOddSize } from "../soundfont/basic_soundfont/riff_chunk.js";
|
|
12
|
-
import { writeLittleEndian } from "./byte_functions/little_endian.js";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
*
|
|
16
|
-
* @param audioBuffer {AudioBuffer}
|
|
17
|
-
* @param normalizeAudio {boolean} find the max sample point and set it to 1, and scale others with it
|
|
18
|
-
* @param channelOffset {number} channel offset and channel offset + 1 get saved
|
|
19
|
-
* @param metadata {WaveMetadata}
|
|
20
|
-
* @param loop {{start: number, end: number}} loop start and end points in seconds. Undefined if no loop
|
|
21
|
-
* @returns {Blob}
|
|
22
|
-
*/
|
|
23
|
-
export function audioBufferToWav(audioBuffer, normalizeAudio = true, channelOffset = 0, metadata = {}, loop = undefined)
|
|
24
|
-
{
|
|
25
|
-
const channel1Data = audioBuffer.getChannelData(channelOffset);
|
|
26
|
-
const channel2Data = audioBuffer.getChannelData(channelOffset + 1);
|
|
27
|
-
const length = channel1Data.length;
|
|
28
|
-
|
|
29
|
-
const bytesPerSample = 2; // 16-bit PCM
|
|
30
|
-
|
|
31
|
-
// prepare INFO chunk
|
|
32
|
-
let infoChunk = new IndexedByteArray(0);
|
|
33
|
-
const infoOn = Object.keys(metadata).length > 0;
|
|
34
|
-
// INFO chunk
|
|
35
|
-
if (infoOn)
|
|
36
|
-
{
|
|
37
|
-
const encoder = new TextEncoder();
|
|
38
|
-
const infoChunks = [
|
|
39
|
-
getStringBytes("INFO"),
|
|
40
|
-
writeRIFFOddSize("ICMT", encoder.encode("Created with SpessaSynth"), true)
|
|
41
|
-
];
|
|
42
|
-
if (metadata.artist)
|
|
43
|
-
{
|
|
44
|
-
infoChunks.push(
|
|
45
|
-
writeRIFFOddSize("IART", encoder.encode(metadata.artist), true)
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
if (metadata.album)
|
|
49
|
-
{
|
|
50
|
-
infoChunks.push(
|
|
51
|
-
writeRIFFOddSize("IPRD", encoder.encode(metadata.album), true)
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
if (metadata.genre)
|
|
55
|
-
{
|
|
56
|
-
infoChunks.push(
|
|
57
|
-
writeRIFFOddSize("IGNR", encoder.encode(metadata.genre), true)
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
if (metadata.title)
|
|
61
|
-
{
|
|
62
|
-
infoChunks.push(
|
|
63
|
-
writeRIFFOddSize("INAM", encoder.encode(metadata.title), true)
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
infoChunk = writeRIFFOddSize("LIST", combineArrays(infoChunks));
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// prepare CUE chunk
|
|
70
|
-
let cueChunk = new IndexedByteArray(0);
|
|
71
|
-
const cueOn = loop?.end !== undefined && loop?.start !== undefined;
|
|
72
|
-
if (cueOn)
|
|
73
|
-
{
|
|
74
|
-
const loopStartSamples = Math.floor(loop.start * audioBuffer.sampleRate);
|
|
75
|
-
const loopEndSamples = Math.floor(loop.end * audioBuffer.sampleRate);
|
|
76
|
-
|
|
77
|
-
const cueStart = new IndexedByteArray(24);
|
|
78
|
-
writeLittleEndian(cueStart, 0, 4); // dwIdentifier
|
|
79
|
-
writeLittleEndian(cueStart, 0, 4); // dwPosition
|
|
80
|
-
writeStringAsBytes(cueStart, "data"); // cue point ID
|
|
81
|
-
writeLittleEndian(cueStart, 0, 4); // chunkStart, always 0
|
|
82
|
-
writeLittleEndian(cueStart, 0, 4); // BlockStart, always 0
|
|
83
|
-
writeLittleEndian(cueStart, loopStartSamples, 4); // sampleOffset
|
|
84
|
-
|
|
85
|
-
const cueEnd = new IndexedByteArray(24);
|
|
86
|
-
writeLittleEndian(cueEnd, 1, 4); // dwIdentifier
|
|
87
|
-
writeLittleEndian(cueEnd, 0, 4); // dwPosition
|
|
88
|
-
writeStringAsBytes(cueEnd, "data"); // cue point ID
|
|
89
|
-
writeLittleEndian(cueEnd, 0, 4); // chunkStart, always 0
|
|
90
|
-
writeLittleEndian(cueEnd, 0, 4); // BlockStart, always 0
|
|
91
|
-
writeLittleEndian(cueEnd, loopEndSamples, 4); // sampleOffset
|
|
92
|
-
|
|
93
|
-
const out = combineArrays([
|
|
94
|
-
new IndexedByteArray([2, 0, 0, 0]), // cue points count,
|
|
95
|
-
cueStart,
|
|
96
|
-
cueEnd
|
|
97
|
-
]);
|
|
98
|
-
cueChunk = writeRIFFOddSize("cue ", out);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Prepare the header
|
|
102
|
-
const headerSize = 44;
|
|
103
|
-
const dataSize = length * 2 * bytesPerSample; // 2 channels, 16-bit per channel
|
|
104
|
-
const fileSize = headerSize + dataSize + infoChunk.length + cueChunk.length - 8; // total file size minus the first 8 bytes
|
|
105
|
-
const header = new Uint8Array(headerSize);
|
|
106
|
-
|
|
107
|
-
// 'RIFF'
|
|
108
|
-
header.set([82, 73, 70, 70], 0);
|
|
109
|
-
// file length
|
|
110
|
-
header.set(
|
|
111
|
-
new Uint8Array([fileSize & 0xff, (fileSize >> 8) & 0xff, (fileSize >> 16) & 0xff, (fileSize >> 24) & 0xff]),
|
|
112
|
-
4
|
|
113
|
-
);
|
|
114
|
-
// 'WAVE'
|
|
115
|
-
header.set([87, 65, 86, 69], 8);
|
|
116
|
-
// 'fmt '
|
|
117
|
-
header.set([102, 109, 116, 32], 12);
|
|
118
|
-
// fmt chunk length
|
|
119
|
-
header.set([16, 0, 0, 0], 16); // 16 for PCM
|
|
120
|
-
// audio format (PCM)
|
|
121
|
-
header.set([1, 0], 20);
|
|
122
|
-
// number of channels (2)
|
|
123
|
-
header.set([2, 0], 22);
|
|
124
|
-
// sample rate
|
|
125
|
-
const sampleRate = audioBuffer.sampleRate;
|
|
126
|
-
header.set(
|
|
127
|
-
new Uint8Array([sampleRate & 0xff, (sampleRate >> 8) & 0xff, (sampleRate >> 16) & 0xff, (sampleRate >> 24) & 0xff]),
|
|
128
|
-
24
|
|
129
|
-
);
|
|
130
|
-
// byte rate (sample rate * block align)
|
|
131
|
-
const byteRate = sampleRate * 2 * bytesPerSample; // 2 channels, 16-bit per channel
|
|
132
|
-
header.set(
|
|
133
|
-
new Uint8Array([byteRate & 0xff, (byteRate >> 8) & 0xff, (byteRate >> 16) & 0xff, (byteRate >> 24) & 0xff]),
|
|
134
|
-
28
|
|
135
|
-
);
|
|
136
|
-
// block align (channels * bytes per sample)
|
|
137
|
-
header.set([4, 0], 32); // 2 channels * 16-bit per channel / 8
|
|
138
|
-
// bits per sample
|
|
139
|
-
header.set([16, 0], 34); // 16-bit
|
|
140
|
-
|
|
141
|
-
// data chunk identifier 'data'
|
|
142
|
-
header.set([100, 97, 116, 97], 36);
|
|
143
|
-
// data chunk length
|
|
144
|
-
header.set(
|
|
145
|
-
new Uint8Array([dataSize & 0xff, (dataSize >> 8) & 0xff, (dataSize >> 16) & 0xff, (dataSize >> 24) & 0xff]),
|
|
146
|
-
40
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
let wavData = new Uint8Array(fileSize + 8);
|
|
150
|
-
let offset = headerSize;
|
|
151
|
-
wavData.set(header, 0);
|
|
152
|
-
|
|
153
|
-
// Interleave audio data (combine channels)
|
|
154
|
-
let multiplier = 32767;
|
|
155
|
-
if (normalizeAudio)
|
|
156
|
-
{
|
|
157
|
-
// find min and max values to prevent clipping when converting to 16 bits
|
|
158
|
-
const maxAbsValue = channel1Data.map((v, i) => Math.max(Math.abs(v), Math.abs(channel2Data[i])))
|
|
159
|
-
.reduce((a, b) => Math.max(a, b));
|
|
160
|
-
multiplier = maxAbsValue > 0 ? (32767 / maxAbsValue) : 1;
|
|
161
|
-
}
|
|
162
|
-
for (let i = 0; i < length; i++)
|
|
163
|
-
{
|
|
164
|
-
// interleave both channels
|
|
165
|
-
const sample1 = Math.min(32767, Math.max(-32768, channel1Data[i] * multiplier));
|
|
166
|
-
const sample2 = Math.min(32767, Math.max(-32768, channel2Data[i] * multiplier));
|
|
167
|
-
|
|
168
|
-
// convert to 16-bit
|
|
169
|
-
wavData[offset++] = sample1 & 0xff;
|
|
170
|
-
wavData[offset++] = (sample1 >> 8) & 0xff;
|
|
171
|
-
wavData[offset++] = sample2 & 0xff;
|
|
172
|
-
wavData[offset++] = (sample2 >> 8) & 0xff;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (infoOn)
|
|
176
|
-
{
|
|
177
|
-
wavData.set(infoChunk, offset);
|
|
178
|
-
offset += infoChunk.length;
|
|
179
|
-
}
|
|
180
|
-
if (cueOn)
|
|
181
|
-
{
|
|
182
|
-
wavData.set(cueChunk, offset);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return new Blob([wavData.buffer], { type: "audio/wav" });
|
|
186
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reads as Big endian
|
|
3
|
-
* @param dataArray {IndexedByteArray}
|
|
4
|
-
* @param bytesAmount {number}
|
|
5
|
-
* @returns {number}
|
|
6
|
-
*/
|
|
7
|
-
export function readBytesAsUintBigEndian(dataArray, bytesAmount)
|
|
8
|
-
{
|
|
9
|
-
let out = 0;
|
|
10
|
-
for (let i = 8 * (bytesAmount - 1); i >= 0; i -= 8)
|
|
11
|
-
{
|
|
12
|
-
out |= (dataArray[dataArray.currentIndex++] << i);
|
|
13
|
-
}
|
|
14
|
-
return out >>> 0;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @param number {number}
|
|
19
|
-
* @param bytesAmount {number}
|
|
20
|
-
* @returns {number[]}
|
|
21
|
-
*/
|
|
22
|
-
export function writeBytesAsUintBigEndian(number, bytesAmount)
|
|
23
|
-
{
|
|
24
|
-
const bytes = new Array(bytesAmount).fill(0);
|
|
25
|
-
for (let i = bytesAmount - 1; i >= 0; i--)
|
|
26
|
-
{
|
|
27
|
-
bytes[i] = number & 0xFF;
|
|
28
|
-
number >>= 8;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return bytes;
|
|
32
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reads as little endian
|
|
3
|
-
* @param dataArray {IndexedByteArray}
|
|
4
|
-
* @param bytesAmount {number}
|
|
5
|
-
* @returns {number}
|
|
6
|
-
*/
|
|
7
|
-
export function readLittleEndian(dataArray, bytesAmount)
|
|
8
|
-
{
|
|
9
|
-
let out = 0;
|
|
10
|
-
for (let i = 0; i < bytesAmount; i++)
|
|
11
|
-
{
|
|
12
|
-
out |= (dataArray[dataArray.currentIndex++] << i * 8);
|
|
13
|
-
}
|
|
14
|
-
// make sure it stays unsigned
|
|
15
|
-
return out >>> 0;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Writes a number as little endian seems to also work for negative numbers so yay?
|
|
20
|
-
* @param dataArray {IndexedByteArray}
|
|
21
|
-
* @param number {number}
|
|
22
|
-
* @param byteTarget {number}
|
|
23
|
-
*/
|
|
24
|
-
export function writeLittleEndian(dataArray, number, byteTarget)
|
|
25
|
-
{
|
|
26
|
-
for (let i = 0; i < byteTarget; i++)
|
|
27
|
-
{
|
|
28
|
-
dataArray[dataArray.currentIndex++] = (number >> (i * 8)) & 0xFF;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* @param dataArray {IndexedByteArray}
|
|
34
|
-
* @param word {number}
|
|
35
|
-
*/
|
|
36
|
-
export function writeWord(dataArray, word)
|
|
37
|
-
{
|
|
38
|
-
dataArray[dataArray.currentIndex++] = word & 0xFF;
|
|
39
|
-
dataArray[dataArray.currentIndex++] = word >> 8;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* @param dataArray {IndexedByteArray}
|
|
44
|
-
* @param dword {number}
|
|
45
|
-
*/
|
|
46
|
-
export function writeDword(dataArray, dword)
|
|
47
|
-
{
|
|
48
|
-
writeLittleEndian(dataArray, dword, 4);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* @param byte1 {number}
|
|
53
|
-
* @param byte2 {number}
|
|
54
|
-
* @returns {number}
|
|
55
|
-
*/
|
|
56
|
-
export function signedInt16(byte1, byte2)
|
|
57
|
-
{
|
|
58
|
-
let val = (byte2 << 8) | byte1;
|
|
59
|
-
if (val > 32767)
|
|
60
|
-
{
|
|
61
|
-
return val - 65536;
|
|
62
|
-
}
|
|
63
|
-
return val;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* @param byte {number}
|
|
68
|
-
* @returns {number}
|
|
69
|
-
*/
|
|
70
|
-
export function signedInt8(byte)
|
|
71
|
-
{
|
|
72
|
-
if (byte > 127)
|
|
73
|
-
{
|
|
74
|
-
return byte - 256;
|
|
75
|
-
}
|
|
76
|
-
return byte;
|
|
77
|
-
}
|