spessasynth_lib 0.0.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/.idea/modules.xml +8 -0
- package/.idea/spessasynth_lib.iml +12 -0
- package/.idea/vcs.xml +6 -0
- package/copy_version.sh +38 -0
- package/index.js +73 -0
- package/package/@types/externals/stbvorbis_sync/stbvorbis_sync.min.d.ts +1 -0
- package/package/@types/index.d.ts +34 -0
- package/package/@types/midi_handler/midi_handler.d.ts +39 -0
- package/package/@types/midi_handler/web_midi_link.d.ts +12 -0
- package/package/@types/midi_parser/midi_data.d.ts +95 -0
- package/package/@types/midi_parser/midi_editor.d.ts +45 -0
- package/package/@types/midi_parser/midi_loader.d.ts +100 -0
- package/package/@types/midi_parser/midi_message.d.ts +154 -0
- package/package/@types/midi_parser/midi_writer.d.ts +6 -0
- package/package/@types/midi_parser/rmidi_writer.d.ts +9 -0
- package/package/@types/midi_parser/used_keys_loaded.d.ts +7 -0
- package/package/@types/sequencer/sequencer.d.ts +180 -0
- package/package/@types/sequencer/worklet_sequencer/sequencer_message.d.ts +28 -0
- package/package/@types/soundfont/read/generators.d.ts +98 -0
- package/package/@types/soundfont/read/instruments.d.ts +50 -0
- package/package/@types/soundfont/read/modulators.d.ts +73 -0
- package/package/@types/soundfont/read/presets.d.ts +87 -0
- package/package/@types/soundfont/read/riff_chunk.d.ts +31 -0
- package/package/@types/soundfont/read/samples.d.ts +134 -0
- package/package/@types/soundfont/read/zones.d.ts +141 -0
- package/package/@types/soundfont/soundfont.d.ts +76 -0
- package/package/@types/soundfont/write/ibag.d.ts +6 -0
- package/package/@types/soundfont/write/igen.d.ts +6 -0
- package/package/@types/soundfont/write/imod.d.ts +6 -0
- package/package/@types/soundfont/write/inst.d.ts +6 -0
- package/package/@types/soundfont/write/pbag.d.ts +6 -0
- package/package/@types/soundfont/write/pgen.d.ts +6 -0
- package/package/@types/soundfont/write/phdr.d.ts +6 -0
- package/package/@types/soundfont/write/pmod.d.ts +6 -0
- package/package/@types/soundfont/write/sdta.d.ts +11 -0
- package/package/@types/soundfont/write/shdr.d.ts +8 -0
- package/package/@types/soundfont/write/soundfont_trimmer.d.ts +6 -0
- package/package/@types/soundfont/write/write.d.ts +21 -0
- package/package/@types/synthetizer/audio_effects/effects_config.d.ts +29 -0
- package/package/@types/synthetizer/audio_effects/fancy_chorus.d.ts +93 -0
- package/package/@types/synthetizer/audio_effects/reverb.d.ts +7 -0
- package/package/@types/synthetizer/synth_event_handler.d.ts +161 -0
- package/package/@types/synthetizer/synthetizer.d.ts +294 -0
- package/package/@types/synthetizer/worklet_system/message_protocol/worklet_message.d.ts +89 -0
- package/package/@types/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.d.ts +134 -0
- package/package/@types/synthetizer/worklet_url.d.ts +5 -0
- package/package/@types/utils/buffer_to_wav.d.ts +8 -0
- package/package/@types/utils/byte_functions/big_endian.d.ts +13 -0
- package/package/@types/utils/byte_functions/little_endian.d.ts +35 -0
- package/package/@types/utils/byte_functions/string.d.ts +22 -0
- package/package/@types/utils/byte_functions/variable_length_quantity.d.ts +12 -0
- package/package/@types/utils/indexed_array.d.ts +21 -0
- package/package/@types/utils/loggin.d.ts +26 -0
- package/package/@types/utils/other.d.ts +32 -0
- package/package/LICENSE +26 -0
- package/package/README.md +84 -0
- package/package/externals/NOTICE +9 -0
- package/package/externals/libvorbis/@types/OggVorbisEncoder.d.ts +34 -0
- package/package/externals/libvorbis/OggVorbisEncoder.min.js +1 -0
- package/package/externals/stbvorbis_sync/@types/stbvorbis_sync.d.ts +12 -0
- package/package/externals/stbvorbis_sync/LICENSE +202 -0
- package/package/externals/stbvorbis_sync/stbvorbis_sync.min.js +1 -0
- package/package/index.js +73 -0
- package/package/midi_handler/README.md +3 -0
- package/package/midi_handler/midi_handler.js +118 -0
- package/package/midi_handler/web_midi_link.js +41 -0
- package/package/midi_parser/README.md +3 -0
- package/package/midi_parser/midi_data.js +121 -0
- package/package/midi_parser/midi_editor.js +557 -0
- package/package/midi_parser/midi_loader.js +502 -0
- package/package/midi_parser/midi_message.js +234 -0
- package/package/midi_parser/midi_writer.js +95 -0
- package/package/midi_parser/rmidi_writer.js +271 -0
- package/package/midi_parser/used_keys_loaded.js +172 -0
- package/package/package.json +43 -0
- package/package/sequencer/README.md +23 -0
- package/package/sequencer/sequencer.js +439 -0
- package/package/sequencer/worklet_sequencer/events.js +92 -0
- package/package/sequencer/worklet_sequencer/play.js +309 -0
- package/package/sequencer/worklet_sequencer/process_event.js +167 -0
- package/package/sequencer/worklet_sequencer/process_tick.js +85 -0
- package/package/sequencer/worklet_sequencer/sequencer_message.js +39 -0
- package/package/sequencer/worklet_sequencer/song_control.js +193 -0
- package/package/sequencer/worklet_sequencer/worklet_sequencer.js +218 -0
- package/package/soundfont/README.md +8 -0
- package/package/soundfont/read/generators.js +212 -0
- package/package/soundfont/read/instruments.js +125 -0
- package/package/soundfont/read/modulators.js +249 -0
- package/package/soundfont/read/presets.js +300 -0
- package/package/soundfont/read/riff_chunk.js +81 -0
- package/package/soundfont/read/samples.js +398 -0
- package/package/soundfont/read/zones.js +310 -0
- package/package/soundfont/soundfont.js +357 -0
- package/package/soundfont/write/ibag.js +39 -0
- package/package/soundfont/write/igen.js +75 -0
- package/package/soundfont/write/imod.js +46 -0
- package/package/soundfont/write/inst.js +34 -0
- package/package/soundfont/write/pbag.js +39 -0
- package/package/soundfont/write/pgen.js +77 -0
- package/package/soundfont/write/phdr.js +42 -0
- package/package/soundfont/write/pmod.js +46 -0
- package/package/soundfont/write/sdta.js +72 -0
- package/package/soundfont/write/shdr.js +54 -0
- package/package/soundfont/write/soundfont_trimmer.js +169 -0
- package/package/soundfont/write/write.js +180 -0
- package/package/synthetizer/README.md +6 -0
- package/package/synthetizer/audio_effects/effects_config.js +21 -0
- package/package/synthetizer/audio_effects/fancy_chorus.js +120 -0
- package/package/synthetizer/audio_effects/impulse_response_2.flac +0 -0
- package/package/synthetizer/audio_effects/reverb.js +24 -0
- package/package/synthetizer/synth_event_handler.js +156 -0
- package/package/synthetizer/synthetizer.js +766 -0
- package/package/synthetizer/worklet_processor.min.js +13 -0
- package/package/synthetizer/worklet_system/README.md +6 -0
- package/package/synthetizer/worklet_system/main_processor.js +363 -0
- package/package/synthetizer/worklet_system/message_protocol/handle_message.js +197 -0
- package/package/synthetizer/worklet_system/message_protocol/message_sending.js +74 -0
- package/package/synthetizer/worklet_system/message_protocol/worklet_message.js +121 -0
- package/package/synthetizer/worklet_system/minify_processor.sh +4 -0
- package/package/synthetizer/worklet_system/worklet_methods/controller_control.js +230 -0
- package/package/synthetizer/worklet_system/worklet_methods/data_entry.js +277 -0
- package/package/synthetizer/worklet_system/worklet_methods/note_off.js +109 -0
- package/package/synthetizer/worklet_system/worklet_methods/note_on.js +91 -0
- package/package/synthetizer/worklet_system/worklet_methods/program_control.js +183 -0
- package/package/synthetizer/worklet_system/worklet_methods/reset_controllers.js +177 -0
- package/package/synthetizer/worklet_system/worklet_methods/snapshot.js +129 -0
- package/package/synthetizer/worklet_system/worklet_methods/system_exclusive.js +272 -0
- package/package/synthetizer/worklet_system/worklet_methods/tuning_control.js +195 -0
- package/package/synthetizer/worklet_system/worklet_methods/vibrato_control.js +29 -0
- package/package/synthetizer/worklet_system/worklet_methods/voice_control.js +233 -0
- package/package/synthetizer/worklet_system/worklet_processor.js +9 -0
- package/package/synthetizer/worklet_system/worklet_utilities/lfo.js +23 -0
- package/package/synthetizer/worklet_system/worklet_utilities/lowpass_filter.js +130 -0
- package/package/synthetizer/worklet_system/worklet_utilities/modulation_envelope.js +73 -0
- package/package/synthetizer/worklet_system/worklet_utilities/modulator_curves.js +86 -0
- package/package/synthetizer/worklet_system/worklet_utilities/stereo_panner.js +81 -0
- package/package/synthetizer/worklet_system/worklet_utilities/unit_converter.js +66 -0
- package/package/synthetizer/worklet_system/worklet_utilities/volume_envelope.js +265 -0
- package/package/synthetizer/worklet_system/worklet_utilities/wavetable_oscillator.js +83 -0
- package/package/synthetizer/worklet_system/worklet_utilities/worklet_modulator.js +234 -0
- package/package/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +116 -0
- package/package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +272 -0
- package/package/synthetizer/worklet_url.js +5 -0
- package/package/utils/README.md +4 -0
- package/package/utils/buffer_to_wav.js +101 -0
- package/package/utils/byte_functions/big_endian.js +28 -0
- package/package/utils/byte_functions/little_endian.js +74 -0
- package/package/utils/byte_functions/string.js +97 -0
- package/package/utils/byte_functions/variable_length_quantity.js +37 -0
- package/package/utils/encode_vorbis.js +30 -0
- package/package/utils/indexed_array.js +41 -0
- package/package/utils/loggin.js +79 -0
- package/package/utils/other.js +54 -0
- package/package.json +43 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { combineArrays, IndexedByteArray } from '../../utils/indexed_array.js'
|
|
2
|
+
import { RiffChunk, writeRIFFChunk } from '../read/riff_chunk.js'
|
|
3
|
+
import { writeStringAsBytes } from '../../utils/byte_functions/string.js'
|
|
4
|
+
import { consoleColors } from '../../utils/other.js'
|
|
5
|
+
import { getIGEN } from './igen.js'
|
|
6
|
+
import { getSDTA } from './sdta.js'
|
|
7
|
+
import { getSHDR } from './shdr.js'
|
|
8
|
+
import { getIMOD } from './imod.js'
|
|
9
|
+
import { getIBAG } from './ibag.js'
|
|
10
|
+
import { getINST } from './inst.js'
|
|
11
|
+
import { getPGEN } from './pgen.js'
|
|
12
|
+
import { getPMOD } from './pmod.js'
|
|
13
|
+
import { getPBAG } from './pbag.js'
|
|
14
|
+
import { getPHDR } from './phdr.js'
|
|
15
|
+
import { writeWord } from '../../utils/byte_functions/little_endian.js'
|
|
16
|
+
import {
|
|
17
|
+
SpessaSynthGroupCollapsed,
|
|
18
|
+
SpessaSynthGroupEnd,
|
|
19
|
+
SpessaSynthInfo,
|
|
20
|
+
} from '../../utils/loggin.js'
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @typedef {Object} SoundFont2WriteOptions
|
|
24
|
+
* @property {boolean} compress - if the soundfont should be compressed with the ogg vorbis codec
|
|
25
|
+
* @property {number} compressionQuality - the vorbis compression quality, from -0.1 to 1
|
|
26
|
+
* @property {EncodeVorbisFunction|undefined} compressionFunction - the encode vorbis function. Can be undefined if not compressing.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @type {SoundFont2WriteOptions}
|
|
31
|
+
*/
|
|
32
|
+
const DEFAULT_WRITE_OPTIONS = {
|
|
33
|
+
compress: false,
|
|
34
|
+
compressionQuality: 0.5,
|
|
35
|
+
compressionFunction: undefined
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Write the soundfont as an .sf2 file. This method is DESTRUCTIVE
|
|
40
|
+
* @this {SoundFont2}
|
|
41
|
+
* @param {SoundFont2WriteOptions} options
|
|
42
|
+
* @returns {Uint8Array}
|
|
43
|
+
*/
|
|
44
|
+
export function write(options = DEFAULT_WRITE_OPTIONS)
|
|
45
|
+
{
|
|
46
|
+
if(options.compress)
|
|
47
|
+
{
|
|
48
|
+
if(typeof options.compressionFunction !== "function")
|
|
49
|
+
{
|
|
50
|
+
throw new TypeError("No compression function supplied but compression enabled.");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
SpessaSynthGroupCollapsed("%cSaving soundfont...",
|
|
54
|
+
consoleColors.info);
|
|
55
|
+
SpessaSynthInfo(`%cCompression: %c${options?.compress || "false"}%c quality: %c${options?.compressionQuality || "none"}`,
|
|
56
|
+
consoleColors.info,
|
|
57
|
+
consoleColors.recognized,
|
|
58
|
+
consoleColors.info,
|
|
59
|
+
consoleColors.recognized)
|
|
60
|
+
SpessaSynthInfo("%cWriting INFO...",
|
|
61
|
+
consoleColors.info);
|
|
62
|
+
/**
|
|
63
|
+
* Write INFO
|
|
64
|
+
* @type {IndexedByteArray[]}
|
|
65
|
+
*/
|
|
66
|
+
const infoArrays = [];
|
|
67
|
+
this.soundFontInfo["ISFT"] = "SpessaSynth"; // ( ͡° ͜ʖ ͡°)
|
|
68
|
+
if(options?.compress)
|
|
69
|
+
{
|
|
70
|
+
this.soundFontInfo["ifil"] = "3.0"; // set version to 3
|
|
71
|
+
}
|
|
72
|
+
for (const [type, data] of Object.entries(this.soundFontInfo))
|
|
73
|
+
{
|
|
74
|
+
if(type === "ifil" || type === "iver")
|
|
75
|
+
{
|
|
76
|
+
const major= parseInt(data.split(".")[0]);
|
|
77
|
+
const minor= parseInt(data.split(".")[1]);
|
|
78
|
+
const ckdata = new IndexedByteArray(4);
|
|
79
|
+
writeWord(ckdata, major);
|
|
80
|
+
writeWord(ckdata, minor);
|
|
81
|
+
infoArrays.push(writeRIFFChunk(new RiffChunk(
|
|
82
|
+
type,
|
|
83
|
+
4,
|
|
84
|
+
ckdata
|
|
85
|
+
)));
|
|
86
|
+
}
|
|
87
|
+
else
|
|
88
|
+
{
|
|
89
|
+
const arr = new IndexedByteArray(data.length);
|
|
90
|
+
writeStringAsBytes(arr, data);
|
|
91
|
+
infoArrays.push(writeRIFFChunk(new RiffChunk(
|
|
92
|
+
type,
|
|
93
|
+
data.length,
|
|
94
|
+
arr
|
|
95
|
+
)));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const combined = combineArrays([
|
|
99
|
+
new IndexedByteArray([73, 78, 70, 79]), // INFO
|
|
100
|
+
...infoArrays
|
|
101
|
+
]);
|
|
102
|
+
const infoChunk = writeRIFFChunk(new RiffChunk("LIST", combined.length, combined));
|
|
103
|
+
|
|
104
|
+
SpessaSynthInfo("%cWriting SDTA...",
|
|
105
|
+
consoleColors.info);
|
|
106
|
+
// write sdata
|
|
107
|
+
const smplStartOffsets = [];
|
|
108
|
+
const smplEndOffsets = [];
|
|
109
|
+
const sdtaChunk = getSDTA.call(this, smplStartOffsets, smplEndOffsets, options?.compress, options?.compressionQuality || 0.5, options.compressionFunction);
|
|
110
|
+
|
|
111
|
+
SpessaSynthInfo("%cWriting PDTA...",
|
|
112
|
+
consoleColors.info);
|
|
113
|
+
// write pdata
|
|
114
|
+
// go in reverse so the indexes are correct
|
|
115
|
+
// instruments
|
|
116
|
+
SpessaSynthInfo("%cWriting SHDR...",
|
|
117
|
+
consoleColors.info);
|
|
118
|
+
const shdrChunk = getSHDR.call(this, smplStartOffsets, smplEndOffsets);
|
|
119
|
+
SpessaSynthInfo("%cWriting IGEN...",
|
|
120
|
+
consoleColors.info);
|
|
121
|
+
const igenChunk = getIGEN.call(this);
|
|
122
|
+
SpessaSynthInfo("%cWriting IMOD...",
|
|
123
|
+
consoleColors.info);
|
|
124
|
+
const imodChunk = getIMOD.call(this);
|
|
125
|
+
SpessaSynthInfo("%cWriting IBAG...",
|
|
126
|
+
consoleColors.info);
|
|
127
|
+
const ibagChunk = getIBAG.call(this);
|
|
128
|
+
SpessaSynthInfo("%cWriting INST...",
|
|
129
|
+
consoleColors.info);
|
|
130
|
+
const instChunk = getINST.call(this);
|
|
131
|
+
// presets
|
|
132
|
+
const pgenChunk = getPGEN.call(this);
|
|
133
|
+
SpessaSynthInfo("%cWriting PMOD...",
|
|
134
|
+
consoleColors.info);
|
|
135
|
+
const pmodChunk = getPMOD.call(this);
|
|
136
|
+
SpessaSynthInfo("%cWriting PBAG...",
|
|
137
|
+
consoleColors.info);
|
|
138
|
+
const pbagChunk = getPBAG.call(this);
|
|
139
|
+
SpessaSynthInfo("%cWriting PHDR...",
|
|
140
|
+
consoleColors.info);
|
|
141
|
+
const phdrChunk = getPHDR.call(this);
|
|
142
|
+
// combine in the sfspec order
|
|
143
|
+
const pdtadata = combineArrays([
|
|
144
|
+
new IndexedByteArray([112, 100, 116, 97]), // "ptda"
|
|
145
|
+
phdrChunk,
|
|
146
|
+
pbagChunk,
|
|
147
|
+
pmodChunk,
|
|
148
|
+
pgenChunk,
|
|
149
|
+
instChunk,
|
|
150
|
+
ibagChunk,
|
|
151
|
+
imodChunk,
|
|
152
|
+
igenChunk,
|
|
153
|
+
shdrChunk
|
|
154
|
+
]);
|
|
155
|
+
const pdtaChunk = writeRIFFChunk(new RiffChunk(
|
|
156
|
+
"LIST",
|
|
157
|
+
pdtadata.length,
|
|
158
|
+
pdtadata
|
|
159
|
+
));
|
|
160
|
+
SpessaSynthInfo("%cWriting the output file...",
|
|
161
|
+
consoleColors.info);
|
|
162
|
+
// finally, combine everything
|
|
163
|
+
const riffdata = combineArrays([
|
|
164
|
+
new IndexedByteArray([115, 102, 98, 107]), // "sfbk"
|
|
165
|
+
infoChunk,
|
|
166
|
+
sdtaChunk,
|
|
167
|
+
pdtaChunk
|
|
168
|
+
]);
|
|
169
|
+
|
|
170
|
+
const main = writeRIFFChunk(new RiffChunk(
|
|
171
|
+
"RIFF",
|
|
172
|
+
riffdata.length,
|
|
173
|
+
riffdata
|
|
174
|
+
));
|
|
175
|
+
SpessaSynthInfo(`%cSaved succesfully! Final file size: %c${main.length}`,
|
|
176
|
+
consoleColors.info,
|
|
177
|
+
consoleColors.recognized)
|
|
178
|
+
SpessaSynthGroupEnd();
|
|
179
|
+
return main;
|
|
180
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
## This is the main synthesizer folder.
|
|
2
|
+
The code here is responsible for making the actual sound.
|
|
3
|
+
This is the heart of the SpessaSynth library.
|
|
4
|
+
- `worklet_system` - the current synthesis system with AudioWorklets.
|
|
5
|
+
|
|
6
|
+
`worklet_processor.min.js` - the minified worklet processor code to import.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { DEFAULT_CHORUS_CONFIG } from './fancy_chorus.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {Object} EffectsConfig
|
|
5
|
+
* @property {boolean} chorusEnabled - indicates if the chorus effect is enabled.
|
|
6
|
+
* @property {ChorusConfig} chorusConfig - the configuration for chorus. Pass undefined to use defaults
|
|
7
|
+
* @property {boolean} reverbEnabled - indicates if the reverb effect is enabled.
|
|
8
|
+
* @property {AudioBuffer} reverbImpulseResponse - the impulse response for the reverb. Pass undefined to use defaults
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @type {EffectsConfig}
|
|
14
|
+
*/
|
|
15
|
+
export const DEFAULT_EFFECTS_CONFIG = {
|
|
16
|
+
chorusEnabled: true,
|
|
17
|
+
chorusConfig: DEFAULT_CHORUS_CONFIG,
|
|
18
|
+
|
|
19
|
+
reverbEnabled: true,
|
|
20
|
+
reverbImpulseResponse: undefined // will load the integrated one
|
|
21
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fancy_chorus.js
|
|
3
|
+
* purpose: creates a simple chorus effect node
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {{
|
|
8
|
+
* oscillator: OscillatorNode,
|
|
9
|
+
* oscillatorGain: GainNode,
|
|
10
|
+
* delay: DelayNode
|
|
11
|
+
* }} ChorusNode
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {Object} ChorusConfig
|
|
16
|
+
* @property {number} nodesAmount - the amount of delay nodes (for each channel) and the corresponding oscillators
|
|
17
|
+
* @property {number} defaultDelay - the initial delay, in seconds
|
|
18
|
+
* @property {number} delayVariation - the difference between delays in the delay nodes
|
|
19
|
+
* @property {number} stereoDifference - the difference of delays between two channels (added to the left channel and subtracted from the right)
|
|
20
|
+
* @property {number} oscillatorFrequency - the initial delay oscillator frequency, in Hz.
|
|
21
|
+
* @property {number} oscillatorFrequencyVariation - the difference between frequencies of oscillators, in Hz
|
|
22
|
+
* @property {number} oscillatorGain - how much will oscillator alter the delay in delay nodes, in seconds
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const NODES_AMOUNT = 4;
|
|
26
|
+
const DEFAULT_DELAY = 0.03;
|
|
27
|
+
const DELAY_VARIATION = 0.01;
|
|
28
|
+
const STEREO_DIFF = 0.02;
|
|
29
|
+
|
|
30
|
+
const OSC_FREQ = 0.3;
|
|
31
|
+
const OSC_FREQ_VARIATION = 0.05;
|
|
32
|
+
const OSC_GAIN = 0.003;
|
|
33
|
+
|
|
34
|
+
export const DEFAULT_CHORUS_CONFIG = {
|
|
35
|
+
nodesAmount: NODES_AMOUNT,
|
|
36
|
+
defaultDelay: DEFAULT_DELAY,
|
|
37
|
+
delayVariation: DELAY_VARIATION,
|
|
38
|
+
stereoDifference: STEREO_DIFF,
|
|
39
|
+
oscillatorFrequency: OSC_FREQ,
|
|
40
|
+
oscillatorFrequencyVariation: OSC_FREQ_VARIATION,
|
|
41
|
+
oscillatorGain: OSC_GAIN
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export class FancyChorus
|
|
45
|
+
{
|
|
46
|
+
/**
|
|
47
|
+
* Creates a fancy chorus effect
|
|
48
|
+
* @param output {AudioNode}
|
|
49
|
+
* @param config {ChorusConfig}
|
|
50
|
+
*/
|
|
51
|
+
constructor(output, config = DEFAULT_CHORUS_CONFIG) {
|
|
52
|
+
const context = output.context;
|
|
53
|
+
|
|
54
|
+
this.input = new ChannelSplitterNode(context, {
|
|
55
|
+
numberOfOutputs: 2
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const merger = new ChannelMergerNode(context, {
|
|
59
|
+
numberOfInputs: 2
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @type {ChorusNode[]}
|
|
64
|
+
*/
|
|
65
|
+
const chorusNodesLeft = [];
|
|
66
|
+
/**
|
|
67
|
+
* @type {ChorusNode[]}
|
|
68
|
+
*/
|
|
69
|
+
const chorusNodesRight = [];
|
|
70
|
+
let freq = config.oscillatorFrequency;
|
|
71
|
+
let delay = config.defaultDelay;
|
|
72
|
+
for (let i = 0; i < config.nodesAmount; i++) {
|
|
73
|
+
// left node
|
|
74
|
+
this.createChorusNode(freq, delay - config.stereoDifference, chorusNodesLeft, 0, merger, 0, context, config);
|
|
75
|
+
// right node
|
|
76
|
+
this.createChorusNode(freq, delay + config.stereoDifference, chorusNodesRight, 1, merger, 1, context, config);
|
|
77
|
+
freq += config.oscillatorFrequencyVariation;
|
|
78
|
+
delay += config.delayVariation;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
merger.connect(output);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @param freq {number}
|
|
86
|
+
* @param delay {number}
|
|
87
|
+
* @param list {ChorusNode[]}
|
|
88
|
+
* @param input {number}
|
|
89
|
+
* @param output {AudioNode}
|
|
90
|
+
* @param outputNum {number}
|
|
91
|
+
* @param context {BaseAudioContext}
|
|
92
|
+
* @param config {ChorusConfig}
|
|
93
|
+
*/
|
|
94
|
+
createChorusNode(freq, delay, list, input, output, outputNum, context, config)
|
|
95
|
+
{
|
|
96
|
+
const oscillator = new OscillatorNode(context, {
|
|
97
|
+
type: 'sine',
|
|
98
|
+
frequency: freq
|
|
99
|
+
});
|
|
100
|
+
const gainNode = new GainNode(context, {
|
|
101
|
+
gain: config.oscillatorGain
|
|
102
|
+
});
|
|
103
|
+
const delayNode = new DelayNode(context, {
|
|
104
|
+
delayTime: delay
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
oscillator.connect(gainNode);
|
|
108
|
+
gainNode.connect(delayNode.delayTime);
|
|
109
|
+
oscillator.start(context.currentTime + delay);
|
|
110
|
+
|
|
111
|
+
this.input.connect(delayNode, input);
|
|
112
|
+
delayNode.connect(output, 0, outputNum);
|
|
113
|
+
|
|
114
|
+
list.push({
|
|
115
|
+
oscillator: oscillator,
|
|
116
|
+
oscillatorGain: gainNode,
|
|
117
|
+
delay: delayNode
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a reverb processor
|
|
3
|
+
* @param context {BaseAudioContext}
|
|
4
|
+
* @param reverbBuffer {AudioBuffer}
|
|
5
|
+
* @returns {ConvolverNode}
|
|
6
|
+
*/
|
|
7
|
+
export function getReverbProcessor(context, reverbBuffer = undefined)
|
|
8
|
+
{
|
|
9
|
+
const convolver = new ConvolverNode(context);
|
|
10
|
+
if(reverbBuffer)
|
|
11
|
+
{
|
|
12
|
+
convolver.buffer = reverbBuffer;
|
|
13
|
+
}
|
|
14
|
+
else
|
|
15
|
+
{
|
|
16
|
+
// resolve relative url
|
|
17
|
+
const impulseURL = new URL("impulse_response_2.flac", import.meta.url);
|
|
18
|
+
fetch(impulseURL).then(async response => {
|
|
19
|
+
const data = await response.arrayBuffer();
|
|
20
|
+
convolver.buffer = await context.decodeAudioData(data);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return convolver;
|
|
24
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* synth_event_handler.js
|
|
3
|
+
* purpose: manages the synthesizer's event system, calling assinged functions when synthesizer requests dispatching the event
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* @typedef {{
|
|
9
|
+
* midiNote: number,
|
|
10
|
+
* channel: number,
|
|
11
|
+
* velocity: number
|
|
12
|
+
* }} NoteOnCallback
|
|
13
|
+
*
|
|
14
|
+
* @typedef {{
|
|
15
|
+
* midiNote: number,
|
|
16
|
+
* channel: number
|
|
17
|
+
* }} NoteOffCallback
|
|
18
|
+
*
|
|
19
|
+
* @typedef {{
|
|
20
|
+
* channel: number,
|
|
21
|
+
* isDrumChannel: boolean
|
|
22
|
+
* }} DrumChangeCallback
|
|
23
|
+
*
|
|
24
|
+
* @typedef {{
|
|
25
|
+
* channel: number,
|
|
26
|
+
* program: number,
|
|
27
|
+
* bank: number,
|
|
28
|
+
* userCalled: boolean
|
|
29
|
+
* }} ProgramChangeCallback
|
|
30
|
+
*
|
|
31
|
+
* @typedef {{
|
|
32
|
+
* channel: number,
|
|
33
|
+
* controllerNumber: number,
|
|
34
|
+
* controllerValue: number
|
|
35
|
+
* }} ControllerChangeCallback
|
|
36
|
+
*
|
|
37
|
+
* @typedef {{
|
|
38
|
+
* channel:number,
|
|
39
|
+
* isMuted: boolean
|
|
40
|
+
* }} MuteChannelCallback
|
|
41
|
+
*
|
|
42
|
+
* @typedef {{
|
|
43
|
+
* presetName: string,
|
|
44
|
+
* bank: number,
|
|
45
|
+
* program: number
|
|
46
|
+
* }[]} PresetListChangeCallback
|
|
47
|
+
*
|
|
48
|
+
*
|
|
49
|
+
* @typedef {{
|
|
50
|
+
* channel: number,
|
|
51
|
+
* MSB: number,
|
|
52
|
+
* LSB: number
|
|
53
|
+
* }} PitchWheelCallback
|
|
54
|
+
*
|
|
55
|
+
* @typedef {{
|
|
56
|
+
* channel: number,
|
|
57
|
+
* pressure: number
|
|
58
|
+
* }} ChannelPressureCallback
|
|
59
|
+
*
|
|
60
|
+
* @typedef {string} SoundfontErrorCallback
|
|
61
|
+
*
|
|
62
|
+
*
|
|
63
|
+
* @typedef {
|
|
64
|
+
* NoteOnCallback |
|
|
65
|
+
* NoteOffCallback |
|
|
66
|
+
* DrumChangeCallback |
|
|
67
|
+
* ProgramChangeCallback |
|
|
68
|
+
* ControllerChangeCallback |
|
|
69
|
+
* MuteChannelCallback |
|
|
70
|
+
* PresetListChangeCallback |
|
|
71
|
+
* PitchWheelCallback |
|
|
72
|
+
* SoundfontErrorCallback |
|
|
73
|
+
* ChannelPressureCallback |
|
|
74
|
+
* undefined
|
|
75
|
+
* } EventCallbackData
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @typedef {
|
|
80
|
+
* "noteon"|
|
|
81
|
+
* "noteoff"|
|
|
82
|
+
* "pitchwheel"|
|
|
83
|
+
* "controllerchange"|
|
|
84
|
+
* "programchange"|
|
|
85
|
+
* "channelpressure"|
|
|
86
|
+
* "polypressure" |
|
|
87
|
+
* "drumchange"|
|
|
88
|
+
* "stopall"|
|
|
89
|
+
* "newchannel"|
|
|
90
|
+
* "mutechannel"|
|
|
91
|
+
* "presetlistchange"|
|
|
92
|
+
* "allcontrollerreset"|
|
|
93
|
+
* "soundfonterror"} EventTypes
|
|
94
|
+
*/
|
|
95
|
+
export class EventHandler
|
|
96
|
+
{
|
|
97
|
+
/**
|
|
98
|
+
* A new synthesizer event handler
|
|
99
|
+
*/
|
|
100
|
+
constructor() {
|
|
101
|
+
/**
|
|
102
|
+
* The main list of events
|
|
103
|
+
* @type {Object<EventTypes, Object<string, function(EventCallbackData)>>}
|
|
104
|
+
*/
|
|
105
|
+
this.events = {
|
|
106
|
+
"noteoff": {},
|
|
107
|
+
"noteon": {},
|
|
108
|
+
"pitchwheel": {},
|
|
109
|
+
"controllerchange": {},
|
|
110
|
+
"programchange": {},
|
|
111
|
+
"channelpressure": {},
|
|
112
|
+
"polypressure": {},
|
|
113
|
+
"drumchange": {},
|
|
114
|
+
"stopall": {},
|
|
115
|
+
"newchannel": {},
|
|
116
|
+
"mutechannel": {},
|
|
117
|
+
"presetlistchange": {},
|
|
118
|
+
"allcontrollerreset": {},
|
|
119
|
+
"soundfonterror": {}
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Adds a new event listener
|
|
125
|
+
* @param name {EventTypes}
|
|
126
|
+
* @param id {string} the unique identifier for the event (to delete it
|
|
127
|
+
* @param callback {function(EventCallbackData)}
|
|
128
|
+
*/
|
|
129
|
+
addEvent(name, id, callback)
|
|
130
|
+
{
|
|
131
|
+
this.events[name][id] = callback;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Removes an event listener
|
|
136
|
+
* @param name {EventTypes}
|
|
137
|
+
* @param id {string}
|
|
138
|
+
*/
|
|
139
|
+
removeEvent(name, id)
|
|
140
|
+
{
|
|
141
|
+
delete this.events[name][id];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Calls the given event
|
|
146
|
+
* @param name {EventTypes}
|
|
147
|
+
* @param eventData {EventCallbackData}
|
|
148
|
+
*/
|
|
149
|
+
callEvent(name, eventData)
|
|
150
|
+
{
|
|
151
|
+
if(this.events[name])
|
|
152
|
+
{
|
|
153
|
+
Object.values(this.events[name]).forEach(ev => ev(eventData));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|