spessasynth_core 1.1.3 → 1.1.5

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 +74 -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,272 +0,0 @@
1
- import { decibelAttenuationToGain, timecentsToSeconds } from './unit_converter.js'
2
- import { generatorTypes } from '../../../soundfont/chunk/generators.js'
3
-
4
- /**
5
- * volume_envelope.js
6
- * purpose: applies a volume envelope for a given voice
7
- */
8
-
9
- /**
10
- * @typedef {Object} WorkletVolumeEnvelope
11
- * @property {number} currentAttenuationDb - current voice attenuation in dB (current sample)
12
- * @property {0|1|2|3|4} state - state of the volume envelope. 0 is delay, 1 is attack, 2 is hold, 3 is decay, 4 is sustain
13
- * @property {number} releaseStartDb - the dB attenuation of the voice when it was released
14
- * @property {number} currentReleaseGain - the current linear gain of the release phase
15
- *
16
- * @property {number} attackDuration - the duration of the attack phase, in seconds
17
- * @property {number} decayDuration - the duration of the decay phase, in seconds
18
- *
19
- * @property {number} attenuation - the absolute attenuation in dB
20
- * @property {number} releaseDuration - the duration of the release phase in seconds
21
- * @property {number} sustainDb - the sustain amount in dB
22
- *
23
- * @property {number} delayEnd - the time when delay ends, in absolute seconds
24
- * @property {number} attackEnd - the time when the attack phase ends, in absolute seconds
25
- * @property {number} holdEnd - the time when the hold phase ends, in absolute seconds
26
- * @property {number} decayEnd - the time when the decay phase ends, in absolute seconds
27
- */
28
-
29
- /**
30
- * @type {WorkletVolumeEnvelope}
31
- */
32
- export const DEFAULT_WORKLET_VOLUME_ENVELOPE = {
33
- attenuation: 100,
34
- currentAttenuationDb: 100,
35
- state: 0,
36
- releaseStartDb: 100,
37
- attackDuration: 0,
38
- decayDuration: 0,
39
- releaseDuration: 0,
40
- sustainDb: 0,
41
- delayEnd: 0,
42
- attackEnd: 0,
43
- holdEnd: 0,
44
- decayEnd: 0,
45
- currentReleaseGain: 1,
46
- }
47
-
48
- export const VOLUME_ENVELOPE_SMOOTHING_FACTOR = 0.001;
49
-
50
- const DB_SILENCE = 100;
51
- const GAIN_SILENCE = 0.005;
52
-
53
- /**
54
- * VOL ENV STATES:
55
- * 0 - delay
56
- * 1 - attack
57
- * 2 - hold/peak
58
- * 3 - decay
59
- * 4 - sustain
60
- * release is indicated by isInRelease property
61
- */
62
-
63
- /**
64
- * Recalculates the times of the volume envelope
65
- * @param voice {WorkletVoice} the voice we're working on
66
- */
67
- export function recalculateVolumeEnvelope(voice)
68
- {
69
- const env = voice.volumeEnvelope;
70
- // calculate durations
71
- env.attackDuration = timecentsToSeconds(voice.modulatedGenerators[generatorTypes.attackVolEnv]);
72
- env.decayDuration = timecentsToSeconds(voice.modulatedGenerators[generatorTypes.decayVolEnv]
73
- + ((60 - voice.midiNote) * voice.modulatedGenerators[generatorTypes.keyNumToVolEnvDecay]));
74
- env.releaseDuration = timecentsToSeconds(voice.modulatedGenerators[generatorTypes.releaseVolEnv]);
75
-
76
- // calculate absolute times (they can change, so we have to recalculate every time
77
- env.attenuation = voice.modulatedGenerators[generatorTypes.initialAttenuation] / 10; // divide by ten to get decibelts
78
- env.sustainDb = voice.volumeEnvelope.attenuation + voice.modulatedGenerators[generatorTypes.sustainVolEnv] / 10;
79
-
80
- // calculate absolute end time
81
- env.delayEnd = timecentsToSeconds(voice.modulatedGenerators[generatorTypes.delayVolEnv]) + voice.startTime;
82
- env.attackEnd = env.attackDuration + env.delayEnd;
83
-
84
- // make sure to take keyNumToVolEnvHold into account!!!
85
- env.holdEnd = timecentsToSeconds(voice.modulatedGenerators[generatorTypes.holdVolEnv]
86
- + ((60 - voice.midiNote) * voice.modulatedGenerators[generatorTypes.keyNumToVolEnvHold]))
87
- + env.attackEnd;
88
-
89
- env.decayEnd = env.decayDuration + env.holdEnd;
90
- // check if voice is in release
91
- if(voice.isInRelease)
92
- {
93
- // calculate the db attenuation at the time of release (not a constant because it can change (ex, volume set to 0, the sound should cut off)
94
- switch (env.state) {
95
- case 0:
96
- env.releaseStartDb = 0;
97
- break;
98
-
99
- case 1:
100
- // attack phase
101
- // attack is linear (in gain) so we need to do get db from that
102
- let elapsed = 1 - ((env.attackEnd - voice.releaseStartTime) / env.attackDuration);
103
- // calculate the gain that the attack would have
104
- let attackGain = elapsed * decibelAttenuationToGain(env.attenuation);
105
-
106
- // turn that into db
107
- env.releaseStartDb = 20 * Math.log10(attackGain) * -1;
108
- break;
109
-
110
- case 2:
111
- env.releaseStartDb = env.attenuation;
112
- break;
113
-
114
- case 3:
115
- env.releaseStartDb = (1 - (env.decayEnd - voice.releaseStartTime) / env.decayDuration) * (env.sustainDb - env.attenuation) + env.attenuation;
116
- break;
117
-
118
- case 4:
119
- env.releaseStartDb = env.sustainDb;
120
- break;
121
-
122
- default:
123
- env.releaseStartDb = env.currentAttenuationDb;
124
- }
125
- }
126
- }
127
-
128
- /**
129
- * Applies volume envelope gain to the given output buffer
130
- * @param voice {WorkletVoice} the voice we're working on
131
- * @param audioBuffer {Float32Array} the audio buffer to modify
132
- * @param currentTime {number} the current audio time
133
- * @param centibelOffset {number} the centibel offset of volume, for modLFOtoVolume
134
- * @param sampleTime {number} single sample time in seconds, usually 1 / 44100 of a second
135
- * @param smoothingFactor {number} the adjusted smoothing factor for the envelope
136
- */
137
-
138
- export function applyVolumeEnvelope(voice, audioBuffer, currentTime, centibelOffset, sampleTime, smoothingFactor)
139
- {
140
- let decibelOffset = centibelOffset / 10;
141
- const env = voice.volumeEnvelope;
142
-
143
- // RELEASE PHASE
144
- if(voice.isInRelease)
145
- {
146
- // release needs a more aggressive smoothing factor as the instant notes don't end instantly when they should
147
- const releaseSmoothingFactor = smoothingFactor * 10;
148
- const releaseStartDb = env.releaseStartDb + decibelOffset;
149
- let elapsedRelease = currentTime - voice.releaseStartTime;
150
- let dbDifference = DB_SILENCE - releaseStartDb;
151
- let gain = env.currentReleaseGain;
152
- for (let i = 0; i < audioBuffer.length; i++)
153
- {
154
- let db = (elapsedRelease / env.releaseDuration) * dbDifference + releaseStartDb;
155
- gain = decibelAttenuationToGain(db + decibelOffset);
156
- env.currentReleaseGain += (gain - env.currentReleaseGain) * releaseSmoothingFactor;
157
- audioBuffer[i] *= env.currentReleaseGain;
158
- elapsedRelease += sampleTime;
159
- }
160
-
161
- if(env.currentReleaseGain <= GAIN_SILENCE)
162
- {
163
- voice.finished = true;
164
- }
165
- return;
166
- }
167
-
168
- if(!voice.hasStarted)
169
- {
170
- voice.startTime = currentTime;
171
- recalculateVolumeEnvelope(voice);
172
- voice.hasStarted = true;
173
- }
174
-
175
- let currentFrameTime = currentTime;
176
- let filledBuffer = 0;
177
- switch(env.state)
178
- {
179
- case 0:
180
- // delay phase, no sound is produced
181
- while(currentFrameTime < env.delayEnd)
182
- {
183
- env.currentAttenuationDb = DB_SILENCE;
184
- audioBuffer[filledBuffer] = 0;
185
-
186
- currentFrameTime += sampleTime;
187
- if(++filledBuffer >= audioBuffer.length)
188
- {
189
- return;
190
- }
191
- }
192
- env.state++;
193
- // fallthrough
194
-
195
- case 1:
196
- // attack phase: ramp from 0 to attenuation
197
- while(currentFrameTime < env.attackEnd)
198
- {
199
- // Special case: linear gain ramp instead of linear db ramp
200
- let linearAttenuation = 1 - (env.attackEnd - currentFrameTime) / env.attackDuration; // 0 to 1
201
- const gain = linearAttenuation * decibelAttenuationToGain(env.attenuation + decibelOffset)
202
- audioBuffer[filledBuffer] *= gain;
203
-
204
- // set current attenuation to peak as its invalid during this phase
205
- env.currentAttenuationDb = env.attenuation;
206
-
207
- currentFrameTime += sampleTime;
208
- if(++filledBuffer >= audioBuffer.length)
209
- {
210
- return;
211
- }
212
- }
213
- env.state++;
214
- // fallthrough
215
-
216
- case 2:
217
- // hold/peak phase: stay at attenuation
218
- while(currentFrameTime < env.holdEnd)
219
- {
220
- const newAttenuation = env.attenuation
221
- + decibelOffset;
222
-
223
- // interpolate attenuation to prevent clicking
224
- env.currentAttenuationDb += (newAttenuation - env.currentAttenuationDb) * smoothingFactor;
225
- audioBuffer[filledBuffer] *= decibelAttenuationToGain(env.currentAttenuationDb);
226
-
227
- currentFrameTime += sampleTime;
228
- if(++filledBuffer >= audioBuffer.length)
229
- {
230
- return;
231
- }
232
- }
233
- env.state++;
234
- // fallthrough
235
-
236
- case 3:
237
- // decay phase: linear ramp from attenuation to sustain
238
- while(currentFrameTime < env.decayEnd)
239
- {
240
- const newAttenuation = (1 - (env.decayEnd - currentFrameTime) / env.decayDuration) * (env.sustainDb - env.attenuation) + env.attenuation
241
- + decibelOffset;
242
-
243
- // interpolate attenuation to prevent clicking
244
- env.currentAttenuationDb += (newAttenuation - env.currentAttenuationDb) * smoothingFactor;
245
- audioBuffer[filledBuffer] *= decibelAttenuationToGain(env.currentAttenuationDb);
246
-
247
- currentFrameTime += sampleTime;
248
- if(++filledBuffer >= audioBuffer.length)
249
- {
250
- return;
251
- }
252
- }
253
- env.state++;
254
- // fallthrough
255
-
256
- case 4:
257
- // sustain phase: stay at sustain
258
- while(true)
259
- {
260
- // interpolate attenuation to prevent clicking
261
- const newAttenuation = env.sustainDb
262
- + decibelOffset;
263
- env.currentAttenuationDb += (newAttenuation - env.currentAttenuationDb) * smoothingFactor;
264
- audioBuffer[filledBuffer] *= decibelAttenuationToGain(env.currentAttenuationDb);
265
- if(++filledBuffer >= audioBuffer.length)
266
- {
267
- return;
268
- }
269
- }
270
-
271
- }
272
- }
@@ -1,83 +0,0 @@
1
- /**
2
- * wavetable_oscillator.js
3
- * purpose: plays back raw audio data at an arbitrary playback rate
4
- */
5
-
6
-
7
- /**
8
- * Fills the output buffer with raw sample data
9
- * @param voice {WorkletVoice} the voice we're working on
10
- * @param sampleData {Float32Array} the sample data to write with
11
- * @param outputBuffer {Float32Array} the output buffer to write to
12
- */
13
- export function getOscillatorData(voice, sampleData, outputBuffer)
14
- {
15
- let cur = voice.sample.cursor;
16
- const loop = (voice.sample.loopingMode === 1) || (voice.sample.loopingMode === 3 && !voice.isInRelease);
17
- const loopLength = voice.sample.loopEnd - voice.sample.loopStart;
18
-
19
- if(loop)
20
- {
21
- for (let i = 0; i < outputBuffer.length; i++) {
22
- // check for loop
23
- while(cur >= voice.sample.loopEnd) {
24
- cur -= loopLength;
25
- }
26
-
27
- // grab the 2 nearest points
28
- const floor = ~~cur;
29
- let ceil = floor + 1;
30
-
31
- while(ceil >= voice.sample.loopEnd) {
32
- ceil -= loopLength;
33
- }
34
-
35
- const fraction = cur - floor;
36
-
37
- // grab the samples and interpolate
38
- const upper = sampleData[ceil];
39
- const lower = sampleData[floor];
40
- outputBuffer[i] = (lower + (upper - lower) * fraction);
41
-
42
- // commented code because it's probably gonna come handy... (it did like 6 times already :/)
43
- // if(isNaN(outputBuffer[i]))
44
- // {
45
- // console.error(voice, upper, lower, floor, ceil, cur)
46
- // throw "NAN ALERT";
47
- // }
48
-
49
- cur += voice.sample.playbackStep * voice.currentTuningCalculated;
50
- }
51
- }
52
- else
53
- {
54
- // check and correct end errors
55
- if(voice.sample.end >= sampleData.length)
56
- {
57
- voice.sample.end = sampleData.length - 1;
58
- }
59
- for (let i = 0; i < outputBuffer.length; i++) {
60
-
61
- // linear interpolation
62
- const floor = ~~cur;
63
- const ceil = floor + 1;
64
-
65
- // flag the voice as finished if needed
66
- if(ceil >= voice.sample.end)
67
- {
68
- voice.finished = true;
69
- return;
70
- }
71
-
72
- const fraction = cur - floor;
73
-
74
- // grab the samples and interpolate
75
- const upper = sampleData[ceil];
76
- const lower = sampleData[floor];
77
- outputBuffer[i] = (lower + (upper - lower) * fraction);
78
-
79
- cur += voice.sample.playbackStep * voice.currentTuningCalculated;
80
- }
81
- }
82
- voice.sample.cursor = cur;
83
- }
@@ -1,175 +0,0 @@
1
- import { modulatorSources } from '../../../soundfont/chunk/modulators.js'
2
- import { getModulatorCurveValue, MOD_PRECOMPUTED_LENGTH } from './modulator_curves.js'
3
- import { NON_CC_INDEX_OFFSET } from './worklet_processor_channel.js'
4
- import {recalculateVolumeEnvelope} from "./volume_envelope.js";
5
-
6
- /**
7
- * worklet_modulator.js
8
- * purpose: precomputes all curve types and computes modulators
9
- */
10
-
11
- /**
12
- * Computes a given modulator
13
- * @param controllerTable {Int16Array} all midi controllers as 14bit values + the non controller indexes, starting at 128
14
- * @param modulator {Modulator} the modulator to compute
15
- * @param midiNote {number} the midiNote of the voice belonging to the modulator
16
- * @param velocity {number} the velocity of the voice belonging to the modulator
17
- * @returns {number} the computed value
18
- */
19
- export function computeWorkletModulator(controllerTable, modulator, midiNote, velocity)
20
- {
21
- if(modulator.transformAmount === 0)
22
- {
23
- return 0;
24
- }
25
- // mapped to 0-16384
26
- let rawSourceValue = 0;
27
- if(modulator.sourceUsesCC)
28
- {
29
- rawSourceValue = controllerTable[modulator.sourceIndex];
30
- }
31
- else
32
- {
33
- const index = modulator.sourceIndex + NON_CC_INDEX_OFFSET;
34
- switch (modulator.sourceIndex)
35
- {
36
- case modulatorSources.noController:
37
- return 0;// fluid_mod.c line 374 (0 times secondary times amount is still zero)
38
-
39
- case modulatorSources.noteOnKeyNum:
40
- rawSourceValue = midiNote << 7;
41
- break;
42
-
43
- case modulatorSources.noteOnVelocity:
44
- case modulatorSources.polyPressure:
45
- rawSourceValue = velocity << 7;
46
- break;
47
-
48
- default:
49
- rawSourceValue = controllerTable[index]; // use the 7 bit value
50
- break;
51
- }
52
-
53
- }
54
-
55
- const sourceValue = transforms[modulator.sourceCurveType][modulator.sourcePolarity][modulator.sourceDirection][rawSourceValue];
56
-
57
- // mapped to 0-127
58
- let rawSecondSrcValue;
59
- if(modulator.secSrcUsesCC)
60
- {
61
- rawSecondSrcValue = controllerTable[modulator.secSrcIndex];
62
- }
63
- else
64
- {
65
- const index = modulator.secSrcIndex + NON_CC_INDEX_OFFSET;
66
- switch (modulator.secSrcIndex)
67
- {
68
- case modulatorSources.noController:
69
- rawSecondSrcValue = 16383;// fluid_mod.c line 376
70
- break;
71
-
72
- case modulatorSources.noteOnKeyNum:
73
- rawSecondSrcValue = midiNote << 7;
74
- break;
75
-
76
- case modulatorSources.noteOnVelocity:
77
- case modulatorSources.polyPressure:
78
- rawSecondSrcValue = velocity << 7;
79
- break;
80
-
81
- default:
82
- rawSecondSrcValue = controllerTable[index];
83
- }
84
-
85
- }
86
- const secondSrcValue = transforms[modulator.secSrcCurveType][modulator.secSrcPolarity][modulator.secSrcDirection][rawSecondSrcValue];
87
-
88
-
89
- // compute the modulator
90
- const computedValue = sourceValue * secondSrcValue * modulator.transformAmount;
91
-
92
- if(modulator.transformType === 2)
93
- {
94
- // abs value
95
- return Math.abs(computedValue);
96
- }
97
- return computedValue;
98
- }
99
-
100
- /**
101
- * Computes all modulators of a given voice
102
- * @param voice {WorkletVoice} the voice to compute modulators for
103
- * @param controllerTable {Int16Array} all midi controllers as 14bit values + the non controller indexes, starting at 128
104
- */
105
- export function computeModulators(voice, controllerTable)
106
- {
107
- voice.modulatedGenerators.set(voice.generators);
108
- voice.modulators.forEach(mod => {
109
- voice.modulatedGenerators[mod.modulatorDestination] += computeWorkletModulator(controllerTable, mod, voice.midiNote, voice.velocity);
110
- });
111
- recalculateVolumeEnvelope(voice);
112
- }
113
-
114
- /**
115
- * as follows: transforms[curveType][polarity][direction] is an array
116
- * @type {Float32Array[][][]}
117
- */
118
- const transforms = [];
119
-
120
- for(let curve = 0; curve < 4; curve++)
121
- {
122
- transforms[curve] =
123
- [
124
- [
125
- new Float32Array(MOD_PRECOMPUTED_LENGTH),
126
- new Float32Array(MOD_PRECOMPUTED_LENGTH)
127
- ],
128
- [
129
- new Float32Array(MOD_PRECOMPUTED_LENGTH),
130
- new Float32Array(MOD_PRECOMPUTED_LENGTH)
131
- ]
132
- ];
133
- for (let i = 0; i < MOD_PRECOMPUTED_LENGTH; i++) {
134
-
135
- // polarity 0 dir 0
136
- transforms[curve][0][0][i] = getModulatorCurveValue(
137
- 0,
138
- curve,
139
- i / MOD_PRECOMPUTED_LENGTH,
140
- 0);
141
- if (isNaN(transforms[curve][0][0][i])) {
142
- transforms[curve][0][0][i] = 1;
143
- }
144
-
145
- // polarity 1 dir 0
146
- transforms[curve][1][0][i] = getModulatorCurveValue(
147
- 0,
148
- curve,
149
- i / MOD_PRECOMPUTED_LENGTH,
150
- 1);
151
- if (isNaN(transforms[curve][1][0][i])) {
152
- transforms[curve][1][0][i] = 1;
153
- }
154
-
155
- // polarity 0 dir 1
156
- transforms[curve][0][1][i] = getModulatorCurveValue(
157
- 1,
158
- curve,
159
- i / MOD_PRECOMPUTED_LENGTH,
160
- 0);
161
- if (isNaN(transforms[curve][0][1][i])) {
162
- transforms[curve][0][1][i] = 1;
163
- }
164
-
165
- // polarity 1 dir 1
166
- transforms[curve][1][1][i] = getModulatorCurveValue(
167
- 1,
168
- curve,
169
- i / MOD_PRECOMPUTED_LENGTH,
170
- 1);
171
- if (isNaN(transforms[curve][1][1][i])) {
172
- transforms[curve][1][1][i] = 1;
173
- }
174
- }
175
- }
@@ -1,106 +0,0 @@
1
- import { midiControllers } from '../../../midi_parser/midi_message.js'
2
- import { modulatorSources } from '../../../soundfont/chunk/modulators.js'
3
- /**
4
- * @typedef {Object} WorkletProcessorChannel
5
- * @property {Int16Array} midiControllers - array of MIDI controller values
6
- * @property {boolean[]} lockedControllers - array indicating if a controller is locked
7
- * @property {Float32Array} customControllers - array of custom (not sf2) control values such as RPN pitch tuning, transpose, modulation depth, etc.
8
- * @property {boolean} holdPedal - indicates whether the hold pedal is active
9
- * @property {boolean} drumChannel - indicates whether the channel is a drum channel
10
- * @property {dataEntryStates} dataEntryState - the current state of the data entry
11
- * @property {number} NRPCoarse - the current coarse value of the Non-Registered Parameter
12
- * @property {number} NRPFine - the current fine value of the Non-Registered Parameter
13
- * @property {number} RPValue - the current value of the Registered Parameter
14
- *
15
- * @property {Preset} preset - the channel's preset
16
- * @property {boolean} lockPreset - indicates whether the program on the channel is locked
17
- *
18
- * @property {boolean} lockVibrato - indicates whether the custom vibrato is locked
19
- * @property {Object} channelVibrato - vibrato settings for the channel
20
- * @property {number} channelVibrato.depth - depth of the vibrato effect (cents)
21
- * @property {number} channelVibrato.delay - delay before the vibrato effect starts (seconds)
22
- * @property {number} channelVibrato.rate - rate of the vibrato oscillation (Hz)
23
-
24
- * @property {boolean} isMuted - indicates whether the channel is muted
25
- * @property {WorkletVoice[]} voices - array of voices currently active on the channel
26
- * @property {WorkletVoice[]} sustainedVoices - array of voices that are sustained on the channel
27
- * @property {WorkletVoice[][][]} cachedVoices - first is midi note, second is velocity. output is an array of WorkletVoices
28
- */
29
-
30
- /**
31
- * Adds a new channel
32
- * @this {Synthesizer}
33
- */
34
- export function addNewChannel()
35
- {
36
- /**
37
- * @type {WorkletProcessorChannel}
38
- */
39
- const channel = {
40
- midiControllers: new Int16Array(CONTROLLER_TABLE_SIZE),
41
- lockedControllers: Array(CONTROLLER_TABLE_SIZE).fill(false),
42
- customControllers: new Float32Array(CUSTOM_CONTROLLER_TABLE_SIZE),
43
-
44
- NRPCoarse: 0,
45
- NRPFine: 0,
46
- RPValue: 0,
47
- dataEntryState: dataEntryStates.Idle,
48
-
49
- voices: [],
50
- sustainedVoices: [],
51
- cachedVoices: [],
52
- preset: this.defaultPreset,
53
-
54
- channelVibrato: {delay: 0, depth: 0, rate: 0},
55
- lockVibrato: false,
56
- holdPedal: false,
57
- isMuted: false,
58
- drumChannel: false,
59
- lockPreset: false,
60
-
61
- }
62
- for (let i = 0; i < 128; i++) {
63
- channel.cachedVoices.push([]);
64
- }
65
- this.workletProcessorChannels.push(channel);
66
- this.resetControllers(this.workletProcessorChannels.length - 1);
67
- }
68
-
69
- export const NON_CC_INDEX_OFFSET = 128;
70
- export const CONTROLLER_TABLE_SIZE = 147;
71
- // an array with preset default values so we can quickly use set() to reset the controllers
72
- export const resetArray = new Int16Array(CONTROLLER_TABLE_SIZE);
73
- // default values (the array is 14 bit so shift the 7 bit values by 7 bits)
74
- resetArray[midiControllers.mainVolume] = 100 << 7;
75
- resetArray[midiControllers.expressionController] = 127 << 7;
76
- resetArray[midiControllers.pan] = 64 << 7;
77
- resetArray[midiControllers.releaseTime] = 64 << 7;
78
- resetArray[midiControllers.brightness] = 64 << 7;
79
- resetArray[midiControllers.effects1Depth] = 40 << 7;
80
- resetArray[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheel] = 8192;
81
- resetArray[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheelRange] = 2 << 7;
82
- resetArray[NON_CC_INDEX_OFFSET + modulatorSources.channelPressure] = 127 << 7;
83
-
84
- /**
85
- * @enum {number}
86
- */
87
- export const dataEntryStates = {
88
- Idle: 0,
89
- RPCoarse: 1,
90
- RPFine: 2,
91
- NRPCoarse: 3,
92
- NRPFine: 4,
93
- DataCoarse: 5,
94
- DataFine: 6
95
- };
96
-
97
-
98
- export const customControllers = {
99
- channelTuning: 0, // cents
100
- channelTranspose: 1, // cents
101
- modulationMultiplier: 2, // cents
102
- masterTuning: 3, // cents
103
- }
104
- export const CUSTOM_CONTROLLER_TABLE_SIZE = Object.keys(customControllers).length;
105
- export const customResetArray = new Float32Array(CUSTOM_CONTROLLER_TABLE_SIZE);
106
- customResetArray[customControllers.modulationMultiplier] = 1;