spessasynth_core 3.27.8 → 4.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.
Files changed (157) hide show
  1. package/README.md +85 -51
  2. package/dist/index.d.ts +4057 -0
  3. package/dist/index.js +17178 -0
  4. package/dist/index.js.map +1 -0
  5. package/package.json +24 -6
  6. package/index.js +0 -132
  7. package/src/externals/README.md +0 -6
  8. package/src/externals/fflate/LICENSE +0 -21
  9. package/src/externals/fflate/fflate.min.js +0 -1
  10. package/src/externals/stbvorbis_sync/@types/stbvorbis_sync.d.ts +0 -12
  11. package/src/externals/stbvorbis_sync/LICENSE +0 -202
  12. package/src/externals/stbvorbis_sync/NOTICE +0 -6
  13. package/src/externals/stbvorbis_sync/stbvorbis_sync.min.js +0 -1
  14. package/src/midi/README.md +0 -32
  15. package/src/midi/basic_midi.js +0 -587
  16. package/src/midi/midi_builder.js +0 -203
  17. package/src/midi/midi_loader.js +0 -321
  18. package/src/midi/midi_message.js +0 -254
  19. package/src/midi/midi_sequence.js +0 -230
  20. package/src/midi/midi_tools/get_note_times.js +0 -154
  21. package/src/midi/midi_tools/midi_editor.js +0 -611
  22. package/src/midi/midi_tools/midi_writer.js +0 -105
  23. package/src/midi/midi_tools/rmidi_writer.js +0 -566
  24. package/src/midi/midi_tools/used_keys_loaded.js +0 -256
  25. package/src/midi/xmf_loader.js +0 -454
  26. package/src/sequencer/README.md +0 -9
  27. package/src/sequencer/events.js +0 -81
  28. package/src/sequencer/play.js +0 -362
  29. package/src/sequencer/process_event.js +0 -165
  30. package/src/sequencer/process_tick.js +0 -104
  31. package/src/sequencer/sequencer_engine.js +0 -372
  32. package/src/sequencer/song_control.js +0 -196
  33. package/src/soundfont/README.md +0 -11
  34. package/src/soundfont/basic_soundfont/basic_global_zone.js +0 -6
  35. package/src/soundfont/basic_soundfont/basic_instrument.js +0 -115
  36. package/src/soundfont/basic_soundfont/basic_instrument_zone.js +0 -45
  37. package/src/soundfont/basic_soundfont/basic_preset.js +0 -313
  38. package/src/soundfont/basic_soundfont/basic_preset_zone.js +0 -39
  39. package/src/soundfont/basic_soundfont/basic_sample.js +0 -477
  40. package/src/soundfont/basic_soundfont/basic_soundbank.js +0 -740
  41. package/src/soundfont/basic_soundfont/basic_zone.js +0 -145
  42. package/src/soundfont/basic_soundfont/generator.js +0 -76
  43. package/src/soundfont/basic_soundfont/generator_types.js +0 -151
  44. package/src/soundfont/basic_soundfont/modulator.js +0 -581
  45. package/src/soundfont/basic_soundfont/riff_chunk.js +0 -195
  46. package/src/soundfont/basic_soundfont/write_dls/art2.js +0 -174
  47. package/src/soundfont/basic_soundfont/write_dls/articulator.js +0 -49
  48. package/src/soundfont/basic_soundfont/write_dls/combine_zones.js +0 -374
  49. package/src/soundfont/basic_soundfont/write_dls/ins.js +0 -85
  50. package/src/soundfont/basic_soundfont/write_dls/lins.js +0 -15
  51. package/src/soundfont/basic_soundfont/write_dls/modulator_converter.js +0 -330
  52. package/src/soundfont/basic_soundfont/write_dls/rgn2.js +0 -120
  53. package/src/soundfont/basic_soundfont/write_dls/wave.js +0 -71
  54. package/src/soundfont/basic_soundfont/write_dls/write_dls.js +0 -124
  55. package/src/soundfont/basic_soundfont/write_dls/wsmp.js +0 -78
  56. package/src/soundfont/basic_soundfont/write_dls/wvpl.js +0 -35
  57. package/src/soundfont/basic_soundfont/write_sf2/ibag.js +0 -60
  58. package/src/soundfont/basic_soundfont/write_sf2/igen.js +0 -91
  59. package/src/soundfont/basic_soundfont/write_sf2/imod.js +0 -62
  60. package/src/soundfont/basic_soundfont/write_sf2/inst.js +0 -42
  61. package/src/soundfont/basic_soundfont/write_sf2/pbag.js +0 -57
  62. package/src/soundfont/basic_soundfont/write_sf2/pgen.js +0 -92
  63. package/src/soundfont/basic_soundfont/write_sf2/phdr.js +0 -61
  64. package/src/soundfont/basic_soundfont/write_sf2/pmod.js +0 -62
  65. package/src/soundfont/basic_soundfont/write_sf2/sdta.js +0 -131
  66. package/src/soundfont/basic_soundfont/write_sf2/shdr.js +0 -77
  67. package/src/soundfont/basic_soundfont/write_sf2/write.js +0 -287
  68. package/src/soundfont/dls/articulator_converter.js +0 -402
  69. package/src/soundfont/dls/dls_destinations.js +0 -38
  70. package/src/soundfont/dls/dls_instrument.js +0 -20
  71. package/src/soundfont/dls/dls_preset.js +0 -43
  72. package/src/soundfont/dls/dls_sample.js +0 -238
  73. package/src/soundfont/dls/dls_soundfont.js +0 -183
  74. package/src/soundfont/dls/dls_sources.js +0 -63
  75. package/src/soundfont/dls/dls_zone.js +0 -89
  76. package/src/soundfont/dls/read_articulation.js +0 -300
  77. package/src/soundfont/dls/read_instrument.js +0 -118
  78. package/src/soundfont/dls/read_instrument_list.js +0 -17
  79. package/src/soundfont/dls/read_lart.js +0 -35
  80. package/src/soundfont/dls/read_region.js +0 -157
  81. package/src/soundfont/dls/read_samples.js +0 -154
  82. package/src/soundfont/load_soundfont.js +0 -21
  83. package/src/soundfont/read_sf2/generators.js +0 -43
  84. package/src/soundfont/read_sf2/instrument_zones.js +0 -75
  85. package/src/soundfont/read_sf2/instruments.js +0 -71
  86. package/src/soundfont/read_sf2/modulators.js +0 -25
  87. package/src/soundfont/read_sf2/preset_zones.js +0 -79
  88. package/src/soundfont/read_sf2/presets.js +0 -80
  89. package/src/soundfont/read_sf2/samples.js +0 -317
  90. package/src/soundfont/read_sf2/soundfont.js +0 -452
  91. package/src/soundfont/read_sf2/zones.js +0 -28
  92. package/src/synthetizer/README.md +0 -7
  93. package/src/synthetizer/audio_engine/README.md +0 -9
  94. package/src/synthetizer/audio_engine/engine_components/compute_modulator.js +0 -289
  95. package/src/synthetizer/audio_engine/engine_components/controller_tables.js +0 -90
  96. package/src/synthetizer/audio_engine/engine_components/dynamic_modulator_system.js +0 -95
  97. package/src/synthetizer/audio_engine/engine_components/enums.js +0 -18
  98. package/src/synthetizer/audio_engine/engine_components/key_modifier_manager.js +0 -151
  99. package/src/synthetizer/audio_engine/engine_components/lfo.js +0 -26
  100. package/src/synthetizer/audio_engine/engine_components/lowpass_filter.js +0 -282
  101. package/src/synthetizer/audio_engine/engine_components/midi_audio_channel.js +0 -551
  102. package/src/synthetizer/audio_engine/engine_components/modulation_envelope.js +0 -181
  103. package/src/synthetizer/audio_engine/engine_components/modulator_curves.js +0 -89
  104. package/src/synthetizer/audio_engine/engine_components/soundfont_manager.js +0 -265
  105. package/src/synthetizer/audio_engine/engine_components/stereo_panner.js +0 -124
  106. package/src/synthetizer/audio_engine/engine_components/unit_converter.js +0 -73
  107. package/src/synthetizer/audio_engine/engine_components/voice.js +0 -525
  108. package/src/synthetizer/audio_engine/engine_components/volume_envelope.js +0 -402
  109. package/src/synthetizer/audio_engine/engine_components/wavetable_oscillator.js +0 -274
  110. package/src/synthetizer/audio_engine/engine_methods/controller_control/controller_change.js +0 -159
  111. package/src/synthetizer/audio_engine/engine_methods/controller_control/master_parameters.js +0 -53
  112. package/src/synthetizer/audio_engine/engine_methods/controller_control/reset_controllers.js +0 -254
  113. package/src/synthetizer/audio_engine/engine_methods/create_midi_channel.js +0 -20
  114. package/src/synthetizer/audio_engine/engine_methods/data_entry/awe32.js +0 -198
  115. package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_coarse.js +0 -281
  116. package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_fine.js +0 -109
  117. package/src/synthetizer/audio_engine/engine_methods/mute_channel.js +0 -17
  118. package/src/synthetizer/audio_engine/engine_methods/note_on.js +0 -220
  119. package/src/synthetizer/audio_engine/engine_methods/portamento_time.js +0 -92
  120. package/src/synthetizer/audio_engine/engine_methods/program_change.js +0 -35
  121. package/src/synthetizer/audio_engine/engine_methods/render_voice.js +0 -214
  122. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/embedded_sound_bank.js +0 -42
  123. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/get_preset.js +0 -0
  124. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/update_preset_list.js +0 -19
  125. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/kill_note.js +0 -23
  126. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/note_off.js +0 -56
  127. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_channels.js +0 -16
  128. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_notes.js +0 -30
  129. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/voice_killing.js +0 -63
  130. package/src/synthetizer/audio_engine/engine_methods/system_exclusive.js +0 -1058
  131. package/src/synthetizer/audio_engine/engine_methods/tuning_control/channel_pressure.js +0 -23
  132. package/src/synthetizer/audio_engine/engine_methods/tuning_control/pitch_wheel.js +0 -31
  133. package/src/synthetizer/audio_engine/engine_methods/tuning_control/poly_pressure.js +0 -29
  134. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_master_tuning.js +0 -15
  135. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_modulation_depth.js +0 -26
  136. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_octave_tuning.js +0 -19
  137. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_tuning.js +0 -27
  138. package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_all_channels.js +0 -15
  139. package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_channel.js +0 -34
  140. package/src/synthetizer/audio_engine/main_processor.js +0 -817
  141. package/src/synthetizer/audio_engine/snapshot/apply_synthesizer_snapshot.js +0 -16
  142. package/src/synthetizer/audio_engine/snapshot/channel_snapshot.js +0 -175
  143. package/src/synthetizer/audio_engine/snapshot/synthesizer_snapshot.js +0 -116
  144. package/src/synthetizer/audio_engine/synth_processor_options.js +0 -18
  145. package/src/synthetizer/synth_constants.js +0 -26
  146. package/src/utils/README.md +0 -8
  147. package/src/utils/buffer_to_wav.js +0 -197
  148. package/src/utils/byte_functions/big_endian.js +0 -32
  149. package/src/utils/byte_functions/little_endian.js +0 -77
  150. package/src/utils/byte_functions/string.js +0 -92
  151. package/src/utils/byte_functions/variable_length_quantity.js +0 -42
  152. package/src/utils/fill_with_defaults.js +0 -21
  153. package/src/utils/indexed_array.js +0 -34
  154. package/src/utils/loggin.js +0 -71
  155. package/src/utils/other.js +0 -92
  156. package/src/utils/sysex_detector.js +0 -58
  157. package/src/utils/xg_hacks.js +0 -193
@@ -1,402 +0,0 @@
1
- import { decibelAttenuationToGain, timecentsToSeconds } from "./unit_converter.js";
2
-
3
-
4
- import { generatorTypes } from "../../../soundfont/basic_soundfont/generator_types.js";
5
-
6
- /**
7
- * volume_envelope.js
8
- * purpose: applies a volume envelope for a given voice
9
- */
10
-
11
- export const VOLUME_ENVELOPE_SMOOTHING_FACTOR = 0.01;
12
-
13
- const DB_SILENCE = 100;
14
- const PERCEIVED_DB_SILENCE = 90;
15
- // around 96 dB of attenuation
16
- const PERCEIVED_GAIN_SILENCE = 0.000015; // can't go lower than that (see #50)
17
-
18
- /**
19
- * VOL ENV STATES:
20
- * 0 - delay
21
- * 1 - attack
22
- * 2 - hold/peak
23
- * 3 - decay
24
- * 4 - sustain
25
- * release indicates by isInRelease property
26
- */
27
-
28
- export class VolumeEnvelope
29
- {
30
- /**
31
- * The envelope's current time in samples
32
- * @type {number}
33
- */
34
- currentSampleTime = 0;
35
- /**
36
- * The sample rate in Hz
37
- * @type {number}
38
- */
39
- sampleRate;
40
- /**
41
- * The current attenuation of the envelope in dB
42
- * @type {number}
43
- */
44
- currentAttenuationDb = DB_SILENCE;
45
- /**
46
- * The current stage of the volume envelope
47
- * @type {0|1|2|3|4}
48
- */
49
- state = 0;
50
- /**
51
- * The dB attenuation of the envelope when it entered the release stage
52
- * @type {number}
53
- */
54
- releaseStartDb = DB_SILENCE;
55
- /**
56
- * The time in samples relative to the start of the envelope
57
- * @type {number}
58
- */
59
- releaseStartTimeSamples = 0;
60
- /**
61
- * The current gain applied to the voice in the release stage
62
- * @type {number}
63
- */
64
- currentReleaseGain = 1;
65
- /**
66
- * The attack duration in samples
67
- * @type {number}
68
- */
69
- attackDuration = 0;
70
- /**
71
- * The decay duration in samples
72
- * @type {number}
73
- */
74
- decayDuration = 0;
75
- /**
76
- * The release duration in samples
77
- * @type {number}
78
- */
79
- releaseDuration = 0;
80
- /**
81
- * The voice's absolute attenuation as linear gain
82
- * @type {number}
83
- */
84
- attenuation = 0;
85
- /**
86
- * The attenuation target, which the "attenuation" property is linearly interpolated towards (gain)
87
- * @type {number}
88
- */
89
- attenuationTargetGain = 0;
90
- /**
91
- * The attenuation target, which the "attenuation" property is linearly interpolated towards (dB)
92
- * @type {number}
93
- */
94
- attenuationTarget = 0;
95
- /**
96
- * The voice's sustain amount in dB, relative to attenuation
97
- * @type {number}
98
- */
99
- sustainDbRelative = 0;
100
- /**
101
- * The time in samples to the end of delay stage, relative to the start of the envelope
102
- * @type {number}
103
- */
104
- delayEnd = 0;
105
- /**
106
- * The time in samples to the end of attack stage, relative to the start of the envelope
107
- * @type {number}
108
- */
109
- attackEnd = 0;
110
- /**
111
- * The time in samples to the end of hold stage, relative to the start of the envelope
112
- * @type {number}
113
- */
114
- holdEnd = 0;
115
- /**
116
- * The time in samples to the end of decay stage, relative to the start of the envelope
117
- * @type {number}
118
- */
119
- decayEnd = 0;
120
-
121
- /**
122
- * @param sampleRate {number} Hz
123
- * @param initialDecay {number} cb
124
- */
125
- constructor(sampleRate, initialDecay)
126
- {
127
- this.sampleRate = sampleRate;
128
- /**
129
- * if sustain stge is silent,
130
- * then we can turn off the voice when it is silent.
131
- * We can't do that with modulated as it can silence the volume and then raise it again, and the voice must keep playing
132
- * @type {boolean}
133
- */
134
- this.canEndOnSilentSustain = initialDecay / 10 >= PERCEIVED_DB_SILENCE;
135
- }
136
-
137
- /**
138
- * Starts the release phase in the envelope
139
- * @param voice {Voice} the voice this envelope belongs to
140
- */
141
- static startRelease(voice)
142
- {
143
- voice.volumeEnvelope.releaseStartTimeSamples = voice.volumeEnvelope.currentSampleTime;
144
- voice.volumeEnvelope.currentReleaseGain = decibelAttenuationToGain(voice.volumeEnvelope.currentAttenuationDb);
145
- VolumeEnvelope.recalculate(voice);
146
- }
147
-
148
- /**
149
- * Recalculates the envelope
150
- * @param voice {Voice} the voice this envelope belongs to
151
- */
152
- static recalculate(voice)
153
- {
154
- const env = voice.volumeEnvelope;
155
- const timecentsToSamples = tc =>
156
- {
157
- return Math.max(0, Math.floor(timecentsToSeconds(tc) * env.sampleRate));
158
- };
159
- // calculate absolute times (they can change so we have to recalculate every time
160
- env.attenuationTarget = Math.max(
161
- 0,
162
- Math.min(voice.modulatedGenerators[generatorTypes.initialAttenuation], 1440)
163
- ) / 10; // divide by ten to get decibels
164
- env.attenuationTargetGain = decibelAttenuationToGain(env.attenuationTarget);
165
- env.sustainDbRelative = Math.min(DB_SILENCE, voice.modulatedGenerators[generatorTypes.sustainVolEnv] / 10);
166
- const sustainDb = Math.min(DB_SILENCE, env.sustainDbRelative);
167
-
168
- // calculate durations
169
- env.attackDuration = timecentsToSamples(voice.modulatedGenerators[generatorTypes.attackVolEnv]);
170
-
171
- // decay: sfspec page 35: the time is for change from attenuation to -100dB,
172
- // therefore, we need to calculate the real time
173
- // (changing from attenuation to sustain instead of -100dB)
174
- const fullChange = voice.modulatedGenerators[generatorTypes.decayVolEnv];
175
- const keyNumAddition = (60 - voice.targetKey) * voice.modulatedGenerators[generatorTypes.keyNumToVolEnvDecay];
176
- const fraction = sustainDb / DB_SILENCE;
177
- env.decayDuration = timecentsToSamples(fullChange + keyNumAddition) * fraction;
178
-
179
- env.releaseDuration = timecentsToSamples(voice.modulatedGenerators[generatorTypes.releaseVolEnv]);
180
-
181
- // calculate absolute end times for the values
182
- env.delayEnd = timecentsToSamples(voice.modulatedGenerators[generatorTypes.delayVolEnv]);
183
- env.attackEnd = env.attackDuration + env.delayEnd;
184
-
185
- // make sure to take keyNumToVolEnvHold into account!
186
- const holdExcursion = (60 - voice.targetKey) * voice.modulatedGenerators[generatorTypes.keyNumToVolEnvHold];
187
- env.holdEnd = timecentsToSamples(voice.modulatedGenerators[generatorTypes.holdVolEnv]
188
- + holdExcursion)
189
- + env.attackEnd;
190
-
191
- env.decayEnd = env.decayDuration + env.holdEnd;
192
-
193
- // if this is the first recalculation and the voice has no attack or delay time, set current db to peak
194
- if (env.state === 0 && env.attackEnd === 0)
195
- {
196
- // env.currentAttenuationDb = env.attenuationTarget;
197
- env.state = 2;
198
- }
199
-
200
- // check if voice is in release
201
- if (voice.isInRelease)
202
- {
203
- // no interpolation this time: force update to actual attenuation and calculate release start from there
204
- //env.attenuation = Math.min(DB_SILENCE, env.attenuationTarget);
205
- const sustainDb = Math.max(0, Math.min(DB_SILENCE, env.sustainDbRelative));
206
- const fraction = sustainDb / DB_SILENCE;
207
- env.decayDuration = timecentsToSamples(fullChange + keyNumAddition) * fraction;
208
-
209
- switch (env.state)
210
- {
211
- case 0:
212
- env.releaseStartDb = DB_SILENCE;
213
- break;
214
-
215
- case 1:
216
- // attack phase: get linear gain of the attack phase when release started
217
- // and turn it into db as we're ramping the db up linearly
218
- // (to make volume go down exponentially)
219
- // attack is linear (in gain) so we need to do get db from that
220
- let elapsed = 1 - ((env.attackEnd - env.releaseStartTimeSamples) / env.attackDuration);
221
- // calculate the gain that the attack would have, so
222
- // turn that into db
223
- env.releaseStartDb = 20 * Math.log10(elapsed) * -1;
224
- break;
225
-
226
- case 2:
227
- env.releaseStartDb = 0;
228
- break;
229
-
230
- case 3:
231
- env.releaseStartDb = (1 - (env.decayEnd - env.releaseStartTimeSamples) / env.decayDuration) * sustainDb;
232
- break;
233
-
234
- case 4:
235
- env.releaseStartDb = sustainDb;
236
- break;
237
- }
238
- env.releaseStartDb = Math.max(0, Math.min(env.releaseStartDb, DB_SILENCE));
239
- if (env.releaseStartDb >= PERCEIVED_DB_SILENCE)
240
- {
241
- voice.finished = true;
242
- }
243
- env.currentReleaseGain = decibelAttenuationToGain(env.releaseStartDb);
244
-
245
- // release: sfspec page 35: the time is for change from attenuation to -100dB,
246
- // therefore, we need to calculate the real time
247
- // (changing from release start to -100dB instead of from peak to -100dB)
248
- const releaseFraction = (DB_SILENCE - env.releaseStartDb) / DB_SILENCE;
249
- env.releaseDuration *= releaseFraction;
250
-
251
- }
252
- }
253
-
254
- /**
255
- * Applies volume envelope gain to the given output buffer
256
- * @param voice {Voice} the voice we're working on
257
- * @param audioBuffer {Float32Array} the audio buffer to modify
258
- * @param centibelOffset {number} the centibel offset of volume, for modLFOtoVolume
259
- * @param smoothingFactor {number} the adjusted smoothing factor for the envelope
260
- * @description essentially we use approach of 100dB is silence, 0dB is peak, and always add attenuation to that (which is interpolated)
261
- */
262
- static apply(voice, audioBuffer, centibelOffset, smoothingFactor)
263
- {
264
- const env = voice.volumeEnvelope;
265
- let decibelOffset = centibelOffset / 10;
266
-
267
- const attenuationSmoothing = smoothingFactor;
268
-
269
- // RELEASE PHASE
270
- if (voice.isInRelease)
271
- {
272
- let elapsedRelease = env.currentSampleTime - env.releaseStartTimeSamples;
273
- if (elapsedRelease >= env.releaseDuration)
274
- {
275
- for (let i = 0; i < audioBuffer.length; i++)
276
- {
277
- audioBuffer[i] = 0;
278
- }
279
- voice.finished = true;
280
- return;
281
- }
282
- let dbDifference = DB_SILENCE - env.releaseStartDb;
283
- for (let i = 0; i < audioBuffer.length; i++)
284
- {
285
- // attenuation interpolation
286
- env.attenuation += (env.attenuationTargetGain - env.attenuation) * attenuationSmoothing;
287
- let db = (elapsedRelease / env.releaseDuration) * dbDifference + env.releaseStartDb;
288
- env.currentReleaseGain = env.attenuation * decibelAttenuationToGain(db + decibelOffset);
289
- audioBuffer[i] *= env.currentReleaseGain;
290
- env.currentSampleTime++;
291
- elapsedRelease++;
292
- }
293
-
294
- if (env.currentReleaseGain <= PERCEIVED_GAIN_SILENCE)
295
- {
296
- voice.finished = true;
297
- }
298
- return;
299
- }
300
-
301
- let filledBuffer = 0;
302
- switch (env.state)
303
- {
304
- case 0:
305
- // delay phase, no sound is produced
306
- while (env.currentSampleTime < env.delayEnd)
307
- {
308
- env.currentAttenuationDb = DB_SILENCE;
309
- audioBuffer[filledBuffer] = 0;
310
-
311
- env.currentSampleTime++;
312
- if (++filledBuffer >= audioBuffer.length)
313
- {
314
- return;
315
- }
316
- }
317
- env.state++;
318
- // fallthrough
319
-
320
- case 1:
321
- // attack phase: ramp from 0 to attenuation
322
- while (env.currentSampleTime < env.attackEnd)
323
- {
324
- // attenuation interpolation
325
- env.attenuation += (env.attenuationTargetGain - env.attenuation) * attenuationSmoothing;
326
-
327
- // Special case: linear gain ramp instead of linear db ramp
328
- let linearAttenuation = 1 - (env.attackEnd - env.currentSampleTime) / env.attackDuration; // 0 to 1
329
- audioBuffer[filledBuffer] *= linearAttenuation * env.attenuation * decibelAttenuationToGain(
330
- decibelOffset);
331
- // set current attenuation to peak as its invalid during this phase
332
- env.currentAttenuationDb = 0;
333
-
334
- env.currentSampleTime++;
335
- if (++filledBuffer >= audioBuffer.length)
336
- {
337
- return;
338
- }
339
- }
340
- env.state++;
341
- // fallthrough
342
-
343
- case 2:
344
- // hold/peak phase: stay at attenuation
345
- while (env.currentSampleTime < env.holdEnd)
346
- {
347
- // attenuation interpolation
348
- env.attenuation += (env.attenuationTargetGain - env.attenuation) * attenuationSmoothing;
349
-
350
- audioBuffer[filledBuffer] *= env.attenuation * decibelAttenuationToGain(decibelOffset);
351
- env.currentAttenuationDb = 0;
352
-
353
- env.currentSampleTime++;
354
- if (++filledBuffer >= audioBuffer.length)
355
- {
356
- return;
357
- }
358
- }
359
- env.state++;
360
- // fallthrough
361
-
362
- case 3:
363
- // decay phase: linear ramp from attenuation to sustain
364
- while (env.currentSampleTime < env.decayEnd)
365
- {
366
- // attenuation interpolation
367
- env.attenuation += (env.attenuationTargetGain - env.attenuation) * attenuationSmoothing;
368
-
369
- env.currentAttenuationDb = (1 - (env.decayEnd - env.currentSampleTime) / env.decayDuration) * env.sustainDbRelative;
370
- audioBuffer[filledBuffer] *= env.attenuation * decibelAttenuationToGain(env.currentAttenuationDb + decibelOffset);
371
-
372
- env.currentSampleTime++;
373
- if (++filledBuffer >= audioBuffer.length)
374
- {
375
- return;
376
- }
377
- }
378
- env.state++;
379
- // fallthrough
380
-
381
- case 4:
382
- if (env.canEndOnSilentSustain && env.sustainDbRelative >= PERCEIVED_DB_SILENCE)
383
- {
384
- voice.finished = true;
385
- }
386
- // sustain phase: stay at sustain
387
- while (true)
388
- {
389
- // attenuation interpolation
390
- env.attenuation += (env.attenuationTargetGain - env.attenuation) * attenuationSmoothing;
391
-
392
- audioBuffer[filledBuffer] *= env.attenuation * decibelAttenuationToGain(env.sustainDbRelative + decibelOffset);
393
- env.currentAttenuationDb = env.sustainDbRelative;
394
- env.currentSampleTime++;
395
- if (++filledBuffer >= audioBuffer.length)
396
- {
397
- return;
398
- }
399
- }
400
- }
401
- }
402
- }
@@ -1,274 +0,0 @@
1
- import { interpolationTypes } from "./enums.js";
2
-
3
- /**
4
- * wavetable_oscillator.js
5
- * purpose: plays back raw audio data at an arbitrary playback rate
6
- */
7
-
8
-
9
- export class WavetableOscillator
10
- {
11
- /**
12
- * Fills the output buffer with raw sample data using a given interpolation
13
- * @param voice {Voice} the voice we're working on
14
- * @param outputBuffer {Float32Array} the output buffer to write to
15
- * @param interpolation {interpolationTypes} the interpolation type
16
- */
17
- static getSample(voice, outputBuffer, interpolation)
18
- {
19
- const step = voice.currentTuningCalculated * voice.sample.playbackStep;
20
- // why not?
21
- if (step === 1)
22
- {
23
- WavetableOscillator.getSampleNearest(voice, outputBuffer, step);
24
- return;
25
- }
26
- switch (interpolation)
27
- {
28
- case interpolationTypes.fourthOrder:
29
- this.getSampleHermite(voice, outputBuffer, step);
30
- return;
31
-
32
- case interpolationTypes.linear:
33
- default:
34
- this.getSampleLinear(voice, outputBuffer, step);
35
- return;
36
-
37
- case interpolationTypes.nearestNeighbor:
38
- WavetableOscillator.getSampleNearest(voice, outputBuffer, step);
39
- return;
40
- }
41
- }
42
-
43
- /**
44
- * Fills the output buffer with raw sample data using linear interpolation
45
- * @param voice {Voice} the voice we're working on
46
- * @param outputBuffer {Float32Array} the output buffer to write to
47
- * @param step {number} the step to advance every sample
48
- */
49
- static getSampleLinear(voice, outputBuffer, step)
50
- {
51
- const sample = voice.sample;
52
- let cur = sample.cursor;
53
- const sampleData = sample.sampleData;
54
-
55
- if (sample.isLooping)
56
- {
57
- const loopLength = sample.loopEnd - sample.loopStart;
58
- for (let i = 0; i < outputBuffer.length; i++)
59
- {
60
- // check for loop
61
- while (cur >= sample.loopEnd)
62
- {
63
- cur -= loopLength;
64
- }
65
-
66
- // grab the 2 nearest points
67
- const floor = ~~cur;
68
- let ceil = floor + 1;
69
-
70
- while (ceil >= sample.loopEnd)
71
- {
72
- ceil -= loopLength;
73
- }
74
-
75
- const fraction = cur - floor;
76
-
77
- // grab the samples and interpolate
78
- const upper = sampleData[ceil];
79
- const lower = sampleData[floor];
80
- outputBuffer[i] = (lower + (upper - lower) * fraction);
81
-
82
- cur += step;
83
- }
84
- }
85
- else
86
- {
87
- for (let i = 0; i < outputBuffer.length; i++)
88
- {
89
-
90
- // linear interpolation
91
- const floor = ~~cur;
92
- const ceil = floor + 1;
93
-
94
- // flag the voice as finished if needed
95
- if (ceil >= sample.end)
96
- {
97
- voice.finished = true;
98
- return;
99
- }
100
-
101
- const fraction = cur - floor;
102
-
103
- // grab the samples and interpolate
104
- const upper = sampleData[ceil];
105
- const lower = sampleData[floor];
106
- outputBuffer[i] = (lower + (upper - lower) * fraction);
107
-
108
- cur += step;
109
- }
110
- }
111
- voice.sample.cursor = cur;
112
- }
113
-
114
- /**
115
- * Fills the output buffer with raw sample data using no interpolation (nearest neighbor)
116
- * @param voice {Voice} the voice we're working on
117
- * @param outputBuffer {Float32Array} the output buffer to write to
118
- * @param step {number} the step to advance every sample
119
- */
120
- static getSampleNearest(voice, outputBuffer, step)
121
- {
122
- const sample = voice.sample;
123
- let cur = sample.cursor;
124
- const sampleData = sample.sampleData;
125
-
126
- if (sample.isLooping)
127
- {
128
- const loopLength = sample.loopEnd - sample.loopStart;
129
- for (let i = 0; i < outputBuffer.length; i++)
130
- {
131
- // check for loop
132
- while (cur >= sample.loopEnd)
133
- {
134
- cur -= loopLength;
135
- }
136
-
137
- // grab the nearest neighbor
138
- let ceil = ~~cur + 1;
139
-
140
- while (ceil >= sample.loopEnd)
141
- {
142
- ceil -= loopLength;
143
- }
144
-
145
- outputBuffer[i] = sampleData[ceil];
146
- cur += step;
147
- }
148
- }
149
- else
150
- {
151
- for (let i = 0; i < outputBuffer.length; i++)
152
- {
153
-
154
- // nearest neighbor
155
- const ceil = ~~cur + 1;
156
-
157
- // flag the voice as finished if needed
158
- if (ceil >= sample.end)
159
- {
160
- voice.finished = true;
161
- return;
162
- }
163
-
164
- outputBuffer[i] = sampleData[ceil];
165
- cur += step;
166
- }
167
- }
168
- sample.cursor = cur;
169
- }
170
-
171
-
172
- /**
173
- * Fills the output buffer with raw sample data using Hermite interpolation
174
- * @param voice {Voice} the voice we're working on
175
- * @param outputBuffer {Float32Array} the output buffer to write to
176
- * @param step {number} the step to advance every sample
177
- */
178
- static getSampleHermite(voice, outputBuffer, step)
179
- {
180
- const sample = voice.sample;
181
- let cur = sample.cursor;
182
- const sampleData = sample.sampleData;
183
-
184
- if (sample.isLooping)
185
- {
186
- const loopLength = sample.loopEnd - sample.loopStart;
187
- for (let i = 0; i < outputBuffer.length; i++)
188
- {
189
- // check for loop (it can exceed the end point multiple times)
190
- while (cur >= sample.loopEnd)
191
- {
192
- cur -= loopLength;
193
- }
194
-
195
- // grab the 4 points
196
- const y0 = ~~cur; // point before the cursor. twice bitwise-not is just a faster Math.floor
197
- let y1 = y0 + 1; // point after the cursor
198
- let y2 = y0 + 2; // point 1 after the cursor
199
- let y3 = y0 + 3; // point 2 after the cursor
200
- const t = cur - y0; // the distance from y0 to cursor [0;1]
201
- // y0 is not handled here
202
- // as it's math.floor of cur which is handled above
203
- if (y1 >= sample.loopEnd)
204
- {
205
- y1 -= loopLength;
206
- }
207
- if (y2 >= sample.loopEnd)
208
- {
209
- y2 -= loopLength;
210
- }
211
- if (y3 >= sample.loopEnd)
212
- {
213
- y3 -= loopLength;
214
- }
215
-
216
- // grab the samples
217
- const xm1 = sampleData[y0];
218
- const x0 = sampleData[y1];
219
- const x1 = sampleData[y2];
220
- const x2 = sampleData[y3];
221
-
222
- // interpolate
223
- // https://www.musicdsp.org/en/latest/Other/93-hermite-interpollation.html
224
- const c = (x1 - xm1) * 0.5;
225
- const v = x0 - x1;
226
- const w = c + v;
227
- const a = w + v + (x2 - x0) * 0.5;
228
- const b = w + a;
229
- outputBuffer[i] = ((((a * t) - b) * t + c) * t + x0);
230
-
231
- cur += step;
232
- }
233
- }
234
- else
235
- {
236
- for (let i = 0; i < outputBuffer.length; i++)
237
- {
238
- // grab the 4 points
239
- const y0 = ~~cur; // point before the cursor. twice bitwise-not is just a faster Math.floor
240
- let y1 = y0 + 1; // point after the cursor
241
- let y2 = y0 + 2; // point 1 after the cursor
242
- let y3 = y0 + 3; // point 2 after the cursor
243
- const t = cur - y0; // the distance from y0 to cursor [0;1]
244
-
245
- // flag as finished if needed
246
- if (y1 >= sample.end ||
247
- y2 >= sample.end ||
248
- y3 >= sample.end)
249
- {
250
- voice.finished = true;
251
- return;
252
- }
253
-
254
- // grab the samples
255
- const xm1 = sampleData[y0];
256
- const x0 = sampleData[y1];
257
- const x1 = sampleData[y2];
258
- const x2 = sampleData[y3];
259
-
260
- // interpolate
261
- // https://www.musicdsp.org/en/latest/Other/93-hermite-interpollation.html
262
- const c = (x1 - xm1) * 0.5;
263
- const v = x0 - x1;
264
- const w = c + v;
265
- const a = w + v + (x2 - x0) * 0.5;
266
- const b = w + a;
267
- outputBuffer[i] = ((((a * t) - b) * t + c) * t + x0);
268
-
269
- cur += step;
270
- }
271
- }
272
- voice.sample.cursor = cur;
273
- }
274
- }