spessasynth_core 1.1.3 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/LICENSE +3 -26
  2. package/README.md +156 -474
  3. package/index.js +73 -8
  4. package/package.json +21 -8
  5. package/src/externals/fflate/LICENSE +21 -0
  6. package/src/externals/fflate/fflate.min.js +1 -0
  7. package/src/externals/stbvorbis_sync/@types/stbvorbis_sync.d.ts +12 -0
  8. package/src/externals/stbvorbis_sync/LICENSE +202 -0
  9. package/src/externals/stbvorbis_sync/NOTICE +6 -0
  10. package/src/externals/stbvorbis_sync/stbvorbis_sync.min.js +1 -0
  11. package/src/midi/README.md +32 -0
  12. package/src/midi/basic_midi.js +567 -0
  13. package/src/midi/midi_builder.js +202 -0
  14. package/src/midi/midi_loader.js +324 -0
  15. package/{spessasynth_core/midi_parser → src/midi}/midi_message.js +58 -35
  16. package/src/midi/midi_sequence.js +224 -0
  17. package/src/midi/midi_tools/get_note_times.js +154 -0
  18. package/src/midi/midi_tools/midi_editor.js +611 -0
  19. package/src/midi/midi_tools/midi_writer.js +99 -0
  20. package/src/midi/midi_tools/rmidi_writer.js +567 -0
  21. package/src/midi/midi_tools/used_keys_loaded.js +238 -0
  22. package/src/midi/xmf_loader.js +454 -0
  23. package/src/sequencer/README.md +5 -0
  24. package/src/sequencer/events.js +81 -0
  25. package/src/sequencer/play.js +349 -0
  26. package/src/sequencer/process_event.js +165 -0
  27. package/{spessasynth_core/sequencer/worklet_sequencer → src/sequencer}/process_tick.js +103 -84
  28. package/src/sequencer/sequencer_engine.js +367 -0
  29. package/src/sequencer/song_control.js +201 -0
  30. package/src/soundfont/README.md +13 -0
  31. package/src/soundfont/basic_soundfont/basic_instrument.js +77 -0
  32. package/src/soundfont/basic_soundfont/basic_preset.js +336 -0
  33. package/src/soundfont/basic_soundfont/basic_sample.js +206 -0
  34. package/src/soundfont/basic_soundfont/basic_soundfont.js +565 -0
  35. package/src/soundfont/basic_soundfont/basic_zone.js +64 -0
  36. package/src/soundfont/basic_soundfont/basic_zones.js +43 -0
  37. package/src/soundfont/basic_soundfont/generator.js +220 -0
  38. package/src/soundfont/basic_soundfont/modulator.js +378 -0
  39. package/src/soundfont/basic_soundfont/riff_chunk.js +149 -0
  40. package/src/soundfont/basic_soundfont/write_dls/art2.js +173 -0
  41. package/src/soundfont/basic_soundfont/write_dls/articulator.js +49 -0
  42. package/src/soundfont/basic_soundfont/write_dls/combine_zones.js +400 -0
  43. package/src/soundfont/basic_soundfont/write_dls/ins.js +103 -0
  44. package/src/soundfont/basic_soundfont/write_dls/lins.js +18 -0
  45. package/src/soundfont/basic_soundfont/write_dls/modulator_converter.js +330 -0
  46. package/src/soundfont/basic_soundfont/write_dls/rgn2.js +121 -0
  47. package/src/soundfont/basic_soundfont/write_dls/wave.js +94 -0
  48. package/src/soundfont/basic_soundfont/write_dls/write_dls.js +119 -0
  49. package/src/soundfont/basic_soundfont/write_dls/wsmp.js +78 -0
  50. package/src/soundfont/basic_soundfont/write_dls/wvpl.js +32 -0
  51. package/src/soundfont/basic_soundfont/write_sf2/ibag.js +39 -0
  52. package/src/soundfont/basic_soundfont/write_sf2/igen.js +80 -0
  53. package/src/soundfont/basic_soundfont/write_sf2/imod.js +46 -0
  54. package/src/soundfont/basic_soundfont/write_sf2/inst.js +34 -0
  55. package/src/soundfont/basic_soundfont/write_sf2/pbag.js +39 -0
  56. package/src/soundfont/basic_soundfont/write_sf2/pgen.js +82 -0
  57. package/src/soundfont/basic_soundfont/write_sf2/phdr.js +42 -0
  58. package/src/soundfont/basic_soundfont/write_sf2/pmod.js +46 -0
  59. package/src/soundfont/basic_soundfont/write_sf2/sdta.js +80 -0
  60. package/src/soundfont/basic_soundfont/write_sf2/shdr.js +55 -0
  61. package/src/soundfont/basic_soundfont/write_sf2/write.js +222 -0
  62. package/src/soundfont/dls/articulator_converter.js +396 -0
  63. package/src/soundfont/dls/dls_destinations.js +38 -0
  64. package/src/soundfont/dls/dls_preset.js +44 -0
  65. package/src/soundfont/dls/dls_sample.js +75 -0
  66. package/src/soundfont/dls/dls_soundfont.js +186 -0
  67. package/src/soundfont/dls/dls_sources.js +62 -0
  68. package/src/soundfont/dls/dls_zone.js +95 -0
  69. package/src/soundfont/dls/read_articulation.js +299 -0
  70. package/src/soundfont/dls/read_instrument.js +121 -0
  71. package/src/soundfont/dls/read_instrument_list.js +17 -0
  72. package/src/soundfont/dls/read_lart.js +35 -0
  73. package/src/soundfont/dls/read_region.js +152 -0
  74. package/src/soundfont/dls/read_samples.js +270 -0
  75. package/src/soundfont/load_soundfont.js +21 -0
  76. package/src/soundfont/read_sf2/generators.js +46 -0
  77. package/{spessasynth_core/soundfont/chunk → src/soundfont/read_sf2}/instruments.js +20 -14
  78. package/src/soundfont/read_sf2/modulators.js +36 -0
  79. package/src/soundfont/read_sf2/presets.js +80 -0
  80. package/src/soundfont/read_sf2/samples.js +304 -0
  81. package/src/soundfont/read_sf2/soundfont.js +305 -0
  82. package/{spessasynth_core/soundfont/chunk → src/soundfont/read_sf2}/zones.js +68 -69
  83. package/src/synthetizer/README.md +7 -0
  84. package/src/synthetizer/audio_engine/README.md +9 -0
  85. package/src/synthetizer/audio_engine/engine_components/compute_modulator.js +266 -0
  86. package/src/synthetizer/audio_engine/engine_components/controller_tables.js +88 -0
  87. package/src/synthetizer/audio_engine/engine_components/key_modifier_manager.js +150 -0
  88. package/{spessasynth_core/synthetizer/worklet_system/worklet_utilities → src/synthetizer/audio_engine/engine_components}/lfo.js +9 -6
  89. package/src/synthetizer/audio_engine/engine_components/lowpass_filter.js +282 -0
  90. package/src/synthetizer/audio_engine/engine_components/midi_audio_channel.js +467 -0
  91. package/src/synthetizer/audio_engine/engine_components/modulation_envelope.js +181 -0
  92. package/{spessasynth_core/synthetizer/worklet_system/worklet_utilities → src/synthetizer/audio_engine/engine_components}/modulator_curves.js +33 -30
  93. package/src/synthetizer/audio_engine/engine_components/soundfont_manager.js +221 -0
  94. package/src/synthetizer/audio_engine/engine_components/stereo_panner.js +120 -0
  95. package/{spessasynth_core/synthetizer/worklet_system/worklet_utilities → src/synthetizer/audio_engine/engine_components}/unit_converter.js +11 -4
  96. package/src/synthetizer/audio_engine/engine_components/voice.js +519 -0
  97. package/src/synthetizer/audio_engine/engine_components/volume_envelope.js +401 -0
  98. package/src/synthetizer/audio_engine/engine_components/wavetable_oscillator.js +263 -0
  99. package/src/synthetizer/audio_engine/engine_methods/controller_control/controller_change.js +132 -0
  100. package/src/synthetizer/audio_engine/engine_methods/controller_control/master_parameters.js +48 -0
  101. package/src/synthetizer/audio_engine/engine_methods/controller_control/reset_controllers.js +241 -0
  102. package/src/synthetizer/audio_engine/engine_methods/create_midi_channel.js +27 -0
  103. package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_coarse.js +253 -0
  104. package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_fine.js +66 -0
  105. package/src/synthetizer/audio_engine/engine_methods/mute_channel.js +17 -0
  106. package/src/synthetizer/audio_engine/engine_methods/note_on.js +175 -0
  107. package/src/synthetizer/audio_engine/engine_methods/portamento_time.js +92 -0
  108. package/src/synthetizer/audio_engine/engine_methods/program_change.js +61 -0
  109. package/src/synthetizer/audio_engine/engine_methods/render_voice.js +196 -0
  110. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/clear_sound_font.js +30 -0
  111. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/get_preset.js +22 -0
  112. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/reload_sound_font.js +28 -0
  113. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/send_preset_list.js +31 -0
  114. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/set_embedded_sound_font.js +21 -0
  115. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/kill_note.js +20 -0
  116. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/note_off.js +55 -0
  117. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_channels.js +16 -0
  118. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_notes.js +30 -0
  119. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/voice_killing.js +63 -0
  120. package/src/synthetizer/audio_engine/engine_methods/system_exclusive.js +776 -0
  121. package/src/synthetizer/audio_engine/engine_methods/tuning_control/channel_pressure.js +24 -0
  122. package/src/synthetizer/audio_engine/engine_methods/tuning_control/pitch_wheel.js +33 -0
  123. package/src/synthetizer/audio_engine/engine_methods/tuning_control/poly_pressure.js +31 -0
  124. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_master_tuning.js +15 -0
  125. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_modulation_depth.js +27 -0
  126. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_octave_tuning.js +19 -0
  127. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_tuning.js +27 -0
  128. package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_all_channels.js +15 -0
  129. package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_channel.js +34 -0
  130. package/src/synthetizer/audio_engine/main_processor.js +804 -0
  131. package/src/synthetizer/audio_engine/snapshot/apply_synthesizer_snapshot.js +15 -0
  132. package/src/synthetizer/audio_engine/snapshot/channel_snapshot.js +175 -0
  133. package/src/synthetizer/audio_engine/snapshot/synthesizer_snapshot.js +116 -0
  134. package/src/synthetizer/synth_constants.js +22 -0
  135. package/{spessasynth_core → src}/utils/README.md +1 -0
  136. package/src/utils/buffer_to_wav.js +185 -0
  137. package/src/utils/byte_functions/big_endian.js +32 -0
  138. package/src/utils/byte_functions/little_endian.js +77 -0
  139. package/src/utils/byte_functions/string.js +107 -0
  140. package/src/utils/byte_functions/variable_length_quantity.js +42 -0
  141. package/src/utils/fill_with_defaults.js +21 -0
  142. package/src/utils/indexed_array.js +52 -0
  143. package/{spessasynth_core → src}/utils/loggin.js +70 -78
  144. package/src/utils/other.js +92 -0
  145. package/src/utils/sysex_detector.js +58 -0
  146. package/src/utils/xg_hacks.js +193 -0
  147. package/.idea/inspectionProfiles/Project_Default.xml +0 -10
  148. package/.idea/jsLibraryMappings.xml +0 -6
  149. package/.idea/modules.xml +0 -8
  150. package/.idea/spessasynth_core.iml +0 -12
  151. package/.idea/vcs.xml +0 -6
  152. package/spessasynth_core/midi_parser/README.md +0 -3
  153. package/spessasynth_core/midi_parser/midi_loader.js +0 -386
  154. package/spessasynth_core/sequencer/sequencer.js +0 -202
  155. package/spessasynth_core/sequencer/worklet_sequencer/play.js +0 -209
  156. package/spessasynth_core/sequencer/worklet_sequencer/process_event.js +0 -120
  157. package/spessasynth_core/sequencer/worklet_sequencer/song_control.js +0 -112
  158. package/spessasynth_core/soundfont/README.md +0 -4
  159. package/spessasynth_core/soundfont/chunk/generators.js +0 -205
  160. package/spessasynth_core/soundfont/chunk/modulators.js +0 -232
  161. package/spessasynth_core/soundfont/chunk/presets.js +0 -264
  162. package/spessasynth_core/soundfont/chunk/riff_chunk.js +0 -46
  163. package/spessasynth_core/soundfont/chunk/samples.js +0 -250
  164. package/spessasynth_core/soundfont/soundfont_parser.js +0 -301
  165. package/spessasynth_core/synthetizer/README.md +0 -6
  166. package/spessasynth_core/synthetizer/synthesizer.js +0 -313
  167. package/spessasynth_core/synthetizer/worklet_system/README.md +0 -3
  168. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/controller_control.js +0 -290
  169. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/data_entry.js +0 -280
  170. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/note_off.js +0 -102
  171. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/note_on.js +0 -77
  172. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/program_control.js +0 -140
  173. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/system_exclusive.js +0 -266
  174. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/tuning_control.js +0 -104
  175. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/vibrato_control.js +0 -29
  176. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/voice_control.js +0 -223
  177. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/lowpass_filter.js +0 -133
  178. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/modulation_envelope.js +0 -73
  179. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/stereo_panner.js +0 -76
  180. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/volume_envelope.js +0 -272
  181. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/wavetable_oscillator.js +0 -83
  182. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/worklet_modulator.js +0 -175
  183. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +0 -106
  184. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +0 -285
  185. package/spessasynth_core/utils/buffer_to_wav.js +0 -70
  186. package/spessasynth_core/utils/byte_functions.js +0 -141
  187. package/spessasynth_core/utils/other.js +0 -49
  188. package/spessasynth_core/utils/shiftable_array.js +0 -26
  189. package/spessasynth_core/utils/stbvorbis_sync.js +0 -1877
@@ -1,223 +0,0 @@
1
- import { generatorTypes } from '../../../soundfont/chunk/generators.js'
2
- import {absCentsToHz, decibelAttenuationToGain, timecentsToSeconds} from '../worklet_utilities/unit_converter.js'
3
- import { getLFOValue } from '../worklet_utilities/lfo.js'
4
- import { customControllers } from '../worklet_utilities/worklet_processor_channel.js'
5
- import { getModEnvValue } from '../worklet_utilities/modulation_envelope.js'
6
- import { getOscillatorData } from '../worklet_utilities/wavetable_oscillator.js'
7
- import { panVoice } from '../worklet_utilities/stereo_panner.js'
8
- import {applyVolumeEnvelope, recalculateVolumeEnvelope} from '../worklet_utilities/volume_envelope.js'
9
- import { applyLowpassFilter } from '../worklet_utilities/lowpass_filter.js'
10
- import { MIN_NOTE_LENGTH } from '../../synthesizer.js'
11
-
12
- const HALF_PI = Math.PI / 2;
13
- export const PAN_SMOOTHING_FACTOR = 0.01;
14
-
15
- /**
16
- * Renders a voice to the stereo output buffer
17
- * @param channel {WorkletProcessorChannel} the voice's channel
18
- * @param voice {WorkletVoice} the voice to render
19
- * @param output {Float32Array[]} the output buffer
20
- * @param reverbOutput {Float32Array[]} output for reverb
21
- * @param chorusOutput {Float32Array[]} output for chorus
22
- * @this {Synthesizer}
23
- */
24
- export function renderVoice(channel, voice, output, reverbOutput, chorusOutput)
25
- {
26
- // check if release
27
- if(!voice.isInRelease)
28
- {
29
- // if not in release, check if the release time is
30
- if (this.currentTime >= voice.releaseStartTime)
31
- {
32
- voice.releaseStartModEnv = voice.currentModEnvValue;
33
- voice.isInRelease = true;
34
- recalculateVolumeEnvelope(voice);
35
- voice.volumeEnvelope.currentReleaseGain = decibelAttenuationToGain(voice.volumeEnvelope.releaseStartDb);
36
- }
37
- }
38
-
39
-
40
- // if the initial attenuation is more than 100dB, skip the voice (it's silent anyway)
41
- if(voice.modulatedGenerators[generatorTypes.initialAttenuation] > 2500)
42
- {
43
- if(voice.isInRelease)
44
- {
45
- voice.finished = true;
46
- }
47
- return;
48
- }
49
-
50
- // TUNING
51
-
52
- // calculate tuning
53
- let cents = voice.modulatedGenerators[generatorTypes.fineTune]
54
- + channel.customControllers[customControllers.channelTuning]
55
- + channel.customControllers[customControllers.channelTranspose]
56
- + channel.customControllers[customControllers.masterTuning];
57
- let semitones = voice.modulatedGenerators[generatorTypes.coarseTune];
58
-
59
- // calculate tuning by key
60
- cents += (voice.targetKey - voice.sample.rootKey) * voice.modulatedGenerators[generatorTypes.scaleTuning];
61
-
62
- // vibrato LFO
63
- const vibratoDepth = voice.modulatedGenerators[generatorTypes.vibLfoToPitch];
64
- if(vibratoDepth > 0)
65
- {
66
- const vibStart = voice.startTime + timecentsToSeconds(voice.modulatedGenerators[generatorTypes.delayVibLFO]);
67
- const vibFreqHz = absCentsToHz(voice.modulatedGenerators[generatorTypes.freqVibLFO]);
68
- const lfoVal = getLFOValue(vibStart, vibFreqHz, this.currentTime);
69
- if(lfoVal)
70
- {
71
- cents += lfoVal * (vibratoDepth * channel.customControllers[customControllers.modulationMultiplier]);
72
- }
73
- }
74
-
75
- // lowpass frequency
76
- let lowpassCents = voice.modulatedGenerators[generatorTypes.initialFilterFc];
77
-
78
- // mod LFO
79
- const modPitchDepth = voice.modulatedGenerators[generatorTypes.modLfoToPitch];
80
- const modVolDepth = voice.modulatedGenerators[generatorTypes.modLfoToVolume];
81
- const modFilterDepth = voice.modulatedGenerators[generatorTypes.modLfoToFilterFc];
82
- let modLfoCentibels = 0;
83
- if(modPitchDepth + modFilterDepth + modVolDepth > 0)
84
- {
85
- const modStart = voice.startTime + timecentsToSeconds(voice.modulatedGenerators[generatorTypes.delayModLFO]);
86
- const modFreqHz = absCentsToHz(voice.modulatedGenerators[generatorTypes.freqModLFO]);
87
- const modLfoValue = getLFOValue(modStart, modFreqHz, this.currentTime);
88
- cents += modLfoValue * (modPitchDepth * channel.customControllers[customControllers.modulationMultiplier]);
89
- modLfoCentibels = modLfoValue * modVolDepth;
90
- lowpassCents += modLfoValue * modFilterDepth;
91
- }
92
-
93
- // channel vibrato (GS NRPN)
94
- if(channel.channelVibrato.depth > 0)
95
- {
96
- const channelVibrato = getLFOValue(voice.startTime + channel.channelVibrato.delay, channel.channelVibrato.rate, this.currentTime);
97
- if(channelVibrato)
98
- {
99
- cents += channelVibrato * channel.channelVibrato.depth;
100
- }
101
- }
102
-
103
- // mod env
104
- const modEnvPitchDepth = voice.modulatedGenerators[generatorTypes.modEnvToPitch];
105
- const modEnvFilterDepth = voice.modulatedGenerators[generatorTypes.modEnvToFilterFc];
106
- const modEnv = getModEnvValue(voice, this.currentTime);
107
- lowpassCents += modEnv * modEnvFilterDepth;
108
- cents += modEnv * modEnvPitchDepth;
109
-
110
- // finally calculate the playback rate
111
- const centsTotal = ~~(cents + semitones * 100);
112
- if(centsTotal !== voice.currentTuningCents)
113
- {
114
- voice.currentTuningCents = centsTotal;
115
- voice.currentTuningCalculated = Math.pow(2, centsTotal / 1200);
116
- }
117
-
118
- // PANNING
119
- const pan = ( (Math.max(-500, Math.min(500, voice.modulatedGenerators[generatorTypes.pan] )) + 500) / 1000) ; // 0 to 1
120
-
121
- // SYNTHESIS
122
- const bufferOut = new Float32Array(output[0].length);
123
-
124
- // wavetable oscillator
125
- getOscillatorData(voice, this.workletDumpedSamplesList[voice.sample.sampleID], bufferOut);
126
-
127
-
128
- // lowpass filter
129
- applyLowpassFilter(voice, bufferOut, lowpassCents, this.sampleRate);
130
-
131
- // volenv
132
- applyVolumeEnvelope(voice, bufferOut, this.currentTime, modLfoCentibels, this.sampleTime, this.volumeEnvelopeSmoothingFactor);
133
-
134
- // pan the voice and write out
135
- voice.currentPan += (pan - voice.currentPan) * this.panSmoothingFactor; // smooth out pan to prevent clicking
136
- const panLeft = Math.cos(HALF_PI * voice.currentPan) * this.panLeft;
137
- const panRight = Math.sin(HALF_PI * voice.currentPan) * this.panRight;
138
- panVoice(
139
- panLeft,
140
- panRight,
141
- bufferOut,
142
- output,
143
- reverbOutput, voice.modulatedGenerators[generatorTypes.reverbEffectsSend],
144
- chorusOutput, voice.modulatedGenerators[generatorTypes.chorusEffectsSend]);
145
- }
146
-
147
- /**
148
- * @param channel {WorkletProcessorChannel}
149
- * @param voice {WorkletVoice}
150
- * @return {number}
151
- */
152
- function getPriority(channel, voice)
153
- {
154
- let priority = 0;
155
- if(channel.drumChannel)
156
- {
157
- // important
158
- priority += 5;
159
- }
160
- if(voice.isInRelease)
161
- {
162
- // not important
163
- priority -= 5;
164
- }
165
- // less velocity = less important
166
- priority += voice.velocity / 25; // map to 0-5
167
- // the newer, more important
168
- priority -= voice.volumeEnvelope.state;
169
- if(voice.isInRelease)
170
- {
171
- priority -= 5;
172
- }
173
- priority -= voice.volumeEnvelope.currentAttenuationDb / 50;
174
- return priority;
175
- }
176
-
177
- /**
178
- * @this {Synthesizer}
179
- * @param amount {number}
180
- */
181
- export function voiceKilling(amount)
182
- {
183
- let allVoices = [];
184
- for (const channel of this.workletProcessorChannels)
185
- {
186
- for (const voice of channel.voices)
187
- {
188
- if (!voice.finished)
189
- {
190
- const priority = getPriority(channel, voice);
191
- allVoices.push({ channel, voice, priority });
192
- }
193
- }
194
- }
195
-
196
- // Step 2: Sort voices by priority (ascending order)
197
- allVoices.sort((a, b) => a.priority - b.priority);
198
- const voicesToRemove = allVoices.slice(0, amount);
199
-
200
- for (const { channel, voice } of voicesToRemove)
201
- {
202
- const index = channel.voices.indexOf(voice);
203
- if (index > -1)
204
- {
205
- channel.voices.splice(index, 1);
206
- }
207
- }
208
- }
209
-
210
- /**
211
- * Stops the voice
212
- * @param voice {WorkletVoice} the voice to stop
213
- * @this {Synthesizer}
214
- */
215
- export function releaseVoice(voice)
216
- {
217
- voice.releaseStartTime = this.currentTime;
218
- // check if the note is shorter than the min note time, if so, extend it
219
- if(voice.releaseStartTime - voice.startTime < MIN_NOTE_LENGTH)
220
- {
221
- voice.releaseStartTime = voice.startTime + MIN_NOTE_LENGTH;
222
- }
223
- }
@@ -1,133 +0,0 @@
1
- import { generatorTypes } from '../../../soundfont/chunk/generators.js'
2
- import { absCentsToHz, decibelAttenuationToGain } from './unit_converter.js'
3
-
4
- /**
5
- * lowpass_filter.js
6
- * purpose: applies a low pass filter to a voice
7
- * note to self: most of this is code is just javascript version of the C code from fluidsynth,
8
- * they are the real smart guys.
9
- * Shoutout to them!
10
- */
11
-
12
- /**
13
- * @typedef {Object} WorkletLowpassFilter
14
- * @property {number} a0 - filter coefficient 1
15
- * @property {number} a1 - filter coefficient 2
16
- * @property {number} a2 - filter coefficient 3
17
- * @property {number} a3 - filter coefficient 4
18
- * @property {number} a4 - filter coefficient 5
19
- * @property {number} x1 - input history 1
20
- * @property {number} x2 - input history 2
21
- * @property {number} y1 - output history 1
22
- * @property {number} y2 - output history 2
23
- * @property {number} reasonanceCb - reasonance in centibels
24
- * @property {number} reasonanceGain - resonance gain
25
- * @property {number} cutoffCents - cutoff frequency in cents
26
- * @property {number} cutoffHz - cutoff frequency in Hz
27
- */
28
-
29
- /**
30
- * @type {WorkletLowpassFilter}
31
- */
32
- export const DEFAULT_WORKLET_LOWPASS_FILTER = {
33
- a0: 0,
34
- a1: 0,
35
- a2: 0,
36
- a3: 0,
37
- a4: 0,
38
-
39
- x1: 0,
40
- x2: 0,
41
- y1: 0,
42
- y2: 0,
43
-
44
- reasonanceCb: 0,
45
- reasonanceGain: 1,
46
- cutoffCents: 13500,
47
- cutoffHz: 20000
48
- }
49
-
50
-
51
- /**
52
- * Applies a low-pass filter to the given buffer
53
- * @param voice {WorkletVoice} the voice we're working on
54
- * @param outputBuffer {Float32Array} the buffer to apply the filter to
55
- * @param cutoffCents {number} cutoff frequency in cents
56
- * @param sampleRate {number} in hertz
57
- * @this {Synthesizer}
58
- */
59
- export function applyLowpassFilter(voice, outputBuffer, cutoffCents, sampleRate)
60
- {
61
- if(cutoffCents > 13499)
62
- {
63
- return; // filter is open
64
- }
65
-
66
- // check if the frequency has changed. if so, calculate new coefficients
67
- if(voice.filter.cutoffCents !== cutoffCents || voice.filter.reasonanceCb !== voice.modulatedGenerators[generatorTypes.initialFilterQ])
68
- {
69
- voice.filter.cutoffCents = cutoffCents;
70
- voice.filter.reasonanceCb = voice.modulatedGenerators[generatorTypes.initialFilterQ];
71
- calculateCoefficients(voice, sampleRate);
72
- }
73
-
74
- // filter the input
75
- for (let i = 0; i < outputBuffer.length; i++) {
76
- let input = outputBuffer[i];
77
- let filtered = voice.filter.a0 * input
78
- + voice.filter.a1 * voice.filter.x1
79
- + voice.filter.a2 * voice.filter.x2
80
- - voice.filter.a3 * voice.filter.y1
81
- - voice.filter.a4 * voice.filter.y2;
82
-
83
- // set buffer
84
- voice.filter.x2 = voice.filter.x1;
85
- voice.filter.x1 = input;
86
- voice.filter.y2 = voice.filter.y1;
87
- voice.filter.y1 = filtered;
88
-
89
- outputBuffer[i] = filtered;
90
- }
91
- }
92
-
93
- /**
94
- * @param voice {WorkletVoice}
95
- * @param sampleRate {number}
96
- */
97
- function calculateCoefficients(voice, sampleRate)
98
- {
99
- voice.filter.cutoffHz = absCentsToHz(voice.filter.cutoffCents);
100
-
101
- // fix cutoff on low frequencies (fluid_iir_filter.c line 392)
102
- if(voice.filter.cutoffHz > 0.45 * sampleRate)
103
- {
104
- voice.filter.cutoffHz = 0.45 * sampleRate;
105
- }
106
-
107
- // adjust the filterQ (fluid_iir_filter.c line 204)
108
- const qDb = (voice.filter.reasonanceCb / 10) - 3.01;
109
- voice.filter.reasonanceGain = decibelAttenuationToGain(-1 * qDb); // -1 because it's attenuation, and we don't want attenuation
110
-
111
- // reduce the gain by the Q factor (fluid_iir_filter.c line 250)
112
- const qGain = 1 / Math.sqrt(voice.filter.reasonanceGain);
113
-
114
-
115
- // code is ported from https://github.com/sinshu/meltysynth/ to work with js. I'm too dumb to understand the math behind this...
116
- let w = 2 * Math.PI * voice.filter.cutoffHz / sampleRate;
117
- let cosw = Math.cos(w);
118
- let alpha = Math.sin(w) / (2 * voice.filter.reasonanceGain);
119
-
120
- let b1 = (1 - cosw) * qGain;
121
- let b0 = b1 / 2;
122
- let b2 = b0;
123
- let a0 = 1 + alpha;
124
- let a1 = -2 * cosw;
125
- let a2 = 1 - alpha;
126
-
127
- // set coefficients
128
- voice.filter.a0 = b0 / a0;
129
- voice.filter.a1 = b1 / a0;
130
- voice.filter.a2 = b2 / a0;
131
- voice.filter.a3 = a1 / a0;
132
- voice.filter.a4 = a2 / a0;
133
- }
@@ -1,73 +0,0 @@
1
- import { timecentsToSeconds } from './unit_converter.js'
2
- import { generatorTypes } from '../../../soundfont/chunk/generators.js'
3
- import { getModulatorCurveValue } from './modulator_curves.js'
4
- import { modulatorCurveTypes } from '../../../soundfont/chunk/modulators.js'
5
-
6
- /**
7
- * modulation_envelope.js
8
- * purpose: calculates the modulation envelope for the given voice
9
- */
10
- const PEAK = 1;
11
-
12
- // 1000 should be precise enough
13
- const CONVEX_ATTACK = new Float32Array(1000);
14
- for (let i = 0; i < CONVEX_ATTACK.length; i++) {
15
- // this makes the db linear ( i think
16
- CONVEX_ATTACK[i] = getModulatorCurveValue(0, modulatorCurveTypes.convex, i / 1000, 0);
17
- }
18
-
19
- /**
20
- * Calculates the current modulation envelope value for the given time and voice
21
- * @param voice {WorkletVoice} the voice we're working on
22
- * @param currentTime {number} in seconds
23
- * @returns {number} modenv value, from 0 to 1
24
- */
25
- export function getModEnvValue(voice, currentTime)
26
- {
27
- // calculate env times
28
- let attack = timecentsToSeconds(voice.modulatedGenerators[generatorTypes.attackModEnv]);
29
- let decay = timecentsToSeconds(voice.modulatedGenerators[generatorTypes.decayModEnv] + ((60 - voice.midiNote) * voice.modulatedGenerators[generatorTypes.keyNumToModEnvDecay]));
30
- let hold = timecentsToSeconds(voice.modulatedGenerators[generatorTypes.holdModEnv] + ((60 - voice.midiNote) * voice.modulatedGenerators[generatorTypes.keyNumToModEnvHold]));
31
-
32
- // calculate absolute times
33
- if(voice.isInRelease && voice.releaseStartTime < currentTime)
34
- {
35
- let release = timecentsToSeconds(voice.modulatedGenerators[generatorTypes.releaseModEnv]);
36
- if(voice.modulatedGenerators[generatorTypes.releaseModEnv] < -7199)
37
- {
38
- // prevent lowpass bugs if release is instant
39
- return voice.releaseStartModEnv;
40
- }
41
- return (1 - (currentTime - voice.releaseStartTime) / release) * voice.releaseStartModEnv;
42
- }
43
-
44
- let sustain = 1 - (voice.modulatedGenerators[generatorTypes.sustainModEnv] / 1000);
45
- let delayEnd = timecentsToSeconds(voice.modulatedGenerators[generatorTypes.delayModEnv]) + voice.startTime;
46
- let attackEnd = attack + delayEnd;
47
- let holdEnd = hold + attackEnd;
48
- let decayEnd = decay + holdEnd;
49
-
50
- let modEnvVal
51
- if(currentTime < delayEnd)
52
- {
53
- modEnvVal = 0; // delay
54
- }
55
- else if(currentTime < attackEnd)
56
- {
57
- modEnvVal = CONVEX_ATTACK[~~((1 - (attackEnd - currentTime) / attack) * 1000)]; // convex attack
58
- }
59
- else if(currentTime < holdEnd)
60
- {
61
- modEnvVal = PEAK; // peak
62
- }
63
- else if(currentTime < decayEnd)
64
- {
65
- modEnvVal = (1 - (decayEnd - currentTime) / decay) * (sustain - PEAK) + PEAK; // decay
66
- }
67
- else
68
- {
69
- modEnvVal = sustain; // sustain
70
- }
71
- voice.currentModEnvValue = modEnvVal;
72
- return modEnvVal;
73
- }
@@ -1,76 +0,0 @@
1
- export const WORKLET_SYSTEM_REVERB_DIVIDER = 200;
2
- export const WORKLET_SYSTEM_CHORUS_DIVIDER = 500;
3
- /**
4
- * stereo_panner.js
5
- * purpose: pans a given voice out to the stereo output and to the effects' outputs
6
- */
7
-
8
- /**
9
- * Pans the voice to the given output buffers
10
- * @param gainLeft {number} the left channel gain
11
- * @param gainRight {number} the right channel gain
12
- * @param inputBuffer {Float32Array} the input buffer in mono
13
- * @param output {Float32Array[]} stereo output buffer
14
- * @param reverb {Float32Array[]} stereo reverb input
15
- * @param reverbLevel {number} 0 to 1000, the level of reverb to send
16
- * @param chorus {Float32Array[]} stereo chorus buttfer
17
- * @param chorusLevel {number} 0 to 1000, the level of chorus to send
18
- */
19
- export function panVoice(gainLeft,
20
- gainRight,
21
- inputBuffer,
22
- output,
23
- reverb,
24
- reverbLevel,
25
- chorus,
26
- chorusLevel)
27
- {
28
- if(isNaN(inputBuffer[0]))
29
- {
30
- return;
31
- }
32
-
33
- if(reverbLevel > 0 && reverb !== undefined)
34
- {
35
- const reverbLeft = reverb[0];
36
- const reverbRight = reverb[1];
37
- // cap reverb
38
- reverbLevel = Math.min(reverbLevel, 1000);
39
- const reverbGain = reverbLevel / WORKLET_SYSTEM_REVERB_DIVIDER;
40
- const reverbLeftGain = gainLeft * reverbGain;
41
- const reverbRightGain = gainRight * reverbGain;
42
- for (let i = 0; i < inputBuffer.length; i++) {
43
- reverbLeft[i] += reverbLeftGain * inputBuffer[i];
44
- reverbRight[i] += reverbRightGain * inputBuffer[i];
45
- }
46
- }
47
-
48
- if(chorusLevel > 0 && chorus !== undefined)
49
- {
50
- const chorusLeft = chorus[0];
51
- const chorusRight = chorus[1];
52
- // cap chorus
53
- chorusLevel = Math.min(chorusLevel, 1000);
54
- const chorusGain = chorusLevel / WORKLET_SYSTEM_CHORUS_DIVIDER;
55
- const chorusLeftGain = gainLeft * chorusGain;
56
- const chorusRightGain = gainRight * chorusGain;
57
- for (let i = 0; i < inputBuffer.length; i++) {
58
- chorusLeft[i] += chorusLeftGain * inputBuffer[i];
59
- chorusRight[i] += chorusRightGain * inputBuffer[i];
60
- }
61
- }
62
-
63
- const leftChannel = output[0];
64
- const rightChannel = output[1];
65
- if(gainLeft > 0)
66
- {
67
- for (let i = 0; i < inputBuffer.length; i++) {
68
- leftChannel[i] += gainLeft * inputBuffer[i];
69
- }
70
- }
71
- if(gainRight > 0) {
72
- for (let i = 0; i < inputBuffer.length; i++) {
73
- rightChannel[i] += gainRight * inputBuffer[i];
74
- }
75
- }
76
- }