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,285 +0,0 @@
1
- /**
2
- * worklet_voice.js
3
- * purpose: prepares workletvoices from sample and generator data and manages sample dumping
4
- * note: sample dumping means sending it over to the AudioWorkletGlobalScope
5
- */
6
-
7
- /**
8
- * @typedef {Object} WorkletSample
9
- * @property {number} sampleID - ID of the sample
10
- * @property {number} playbackStep - current playback step (rate)
11
- * @property {number} cursor - current position in the sample
12
- * @property {number} rootKey - root key of the sample
13
- * @property {number} loopStart - start position of the loop
14
- * @property {number} loopEnd - end position of the loop
15
- * @property {number} end - end position of the sample
16
- * @property {0|1|2} loopingMode - looping mode of the sample
17
- */
18
-
19
- /**
20
- * @typedef {Object} WorkletVoice
21
- * @property {WorkletSample} sample - sample ID for voice.
22
- * @property {WorkletLowpassFilter} filter - lowpass filter applied to the voice
23
- * @property {Int16Array} generators - the unmodulated (constant) generators of the voice
24
- * @property {Modulator[]} modulators - the voice's modulators
25
- * @property {Int16Array} modulatedGenerators - the generators modulated by the modulators
26
- *
27
- * @property {boolean} finished - indicates if the voice has finished
28
- * @property {boolean} isInRelease - indicates if the voice is in the release phase
29
- * @property {boolean} hasStarted - indicates if the voice has started rendering
30
- *
31
- * @property {number} channelNumber - MIDI channel number
32
- * @property {number} velocity - velocity of the note
33
- * @property {number} midiNote - MIDI note number
34
- * @property {number} targetKey - target key for the note
35
- *
36
- * @property {WorkletVolumeEnvelope} volumeEnvelope
37
- *
38
- * @property {number} currentModEnvValue - current value of the modulation envelope
39
- * @property {number} releaseStartModEnv - modenv value at the start of the release phase
40
- *
41
- * @property {number} startTime - start time of the voice
42
- * @property {number} releaseStartTime - start time of the release phase
43
- *
44
- * @property {number} currentTuningCents - current tuning adjustment in cents
45
- * @property {number} currentTuningCalculated - calculated tuning adjustment
46
- * @property {number} currentPan - from 0 to 1
47
- */
48
-
49
- import { addAndClampGenerator, generatorTypes } from '../../../soundfont/chunk/generators.js'
50
- import { SpessaSynthTable } from '../../../utils/loggin.js'
51
- import { DEFAULT_WORKLET_VOLUME_ENVELOPE } from './volume_envelope.js'
52
- import { DEFAULT_WORKLET_LOWPASS_FILTER } from './lowpass_filter.js'
53
-
54
-
55
- /**
56
- * the sampleID is the index
57
- * @type {boolean[]}
58
- */
59
- let globalDumpedSamplesList = [];
60
-
61
- export function clearSamplesList()
62
- {
63
- globalDumpedSamplesList = [];
64
- }
65
-
66
- function /**
67
- * This is how the logic works: since sf3 is compressed, we rely on an async decoder.
68
- * So, if the sample isn't loaded yet:
69
- * send the workletVoice (generators, modulators, etc) and the WorkletSample(sampleID + end offset + loop)
70
- * once the voice is done, then we dump it.
71
- *
72
- * on the WorkletScope side:
73
- * skip the voice if sampleID isn't valid
74
- * once we receive a sample dump, adjust all voice endOffsets (loop is already correct in sf3)
75
- * now the voice starts playing, yay!
76
- * @param channel {number} channel hint for the processor to recalculate cursor positions
77
- * @param sample {Sample}
78
- * @param id {number}
79
- * @param sampleDumpCallback {function({channel: number, sampleID: number, sampleData: Float32Array})}
80
- */
81
- dumpSample(channel, sample, id, sampleDumpCallback)
82
- {
83
- // flag as defined, so it's currently being dumped
84
- globalDumpedSamplesList[id] = false;
85
-
86
- // load the data
87
- sampleDumpCallback({
88
- channel: channel,
89
- sampleID: id,
90
- sampleData: sample.getAudioData()
91
- });
92
- globalDumpedSamplesList[id] = true;
93
- }
94
-
95
- /**
96
- * Deep clone function for the WorkletVoice object and its nested structures.
97
- * This function handles Int16Array, objects, arrays, and primitives.
98
- * It does not handle circular references.
99
- * @param {WorkletVoice} obj - The object to clone.
100
- * @returns {WorkletVoice} - Cloned object.
101
- */
102
- function deepClone(obj) {
103
- if (obj === null || typeof obj !== 'object') {
104
- return obj;
105
- }
106
-
107
- // Handle Int16Array separately
108
- if (obj instanceof Int16Array) {
109
- return new Int16Array(obj);
110
- }
111
-
112
- // Handle objects and arrays
113
- const clonedObj = Array.isArray(obj) ? [] : {};
114
- for (let key in obj) {
115
- if (obj.hasOwnProperty(key)) {
116
- if (typeof obj[key] === 'object' && obj[key] !== null) {
117
- clonedObj[key] = deepClone(obj[key]); // Recursively clone nested objects
118
- } else if (obj[key] instanceof Int16Array) {
119
- clonedObj[key] = new Int16Array(obj[key]); // Clone Int16Array
120
- } else {
121
- clonedObj[key] = obj[key]; // Copy primitives
122
- }
123
- }
124
- }
125
- return clonedObj;
126
- }
127
-
128
-
129
- /**
130
- * @param channel {number} a hint for the processor to recalculate sample cursors when sample dumping
131
- * @param midiNote {number}
132
- * @param velocity {number}
133
- * @param preset {Preset}
134
- * @param currentTime {number}
135
- * @param sampleRate {number}
136
- * @param sampleDumpCallback {function({channel: number, sampleID: number, sampleData: Float32Array})}
137
- * @param cachedVoices {WorkletVoice[][][]} first is midi note, second is velocity. output is an array of WorkletVoices
138
- * @param debug {boolean}
139
- * @returns {WorkletVoice[]}
140
- */
141
- export function getWorkletVoices(channel,
142
- midiNote,
143
- velocity,
144
- preset,
145
- currentTime,
146
- sampleRate,
147
- sampleDumpCallback,
148
- cachedVoices,
149
- debug=false)
150
- {
151
- /**
152
- * @type {WorkletVoice[]}
153
- */
154
- let workletVoices;
155
-
156
- const cached = cachedVoices[midiNote][velocity];
157
- if(cached)
158
- {
159
- workletVoices = cached.map(deepClone);
160
- workletVoices.forEach(v => {
161
- v.startTime = currentTime;
162
- });
163
- }
164
- else
165
- {
166
- let canCache = true;
167
- /**
168
- * @returns {WorkletVoice}
169
- */
170
- workletVoices = preset.getSamplesAndGenerators(midiNote, velocity).map(sampleAndGenerators => {
171
- // dump the sample if haven't already
172
- if (globalDumpedSamplesList[sampleAndGenerators.sampleID] !== true) {
173
- // if the sample is currently being loaded, don't dump again (undefined means not loaded, false means is being loaded)
174
- if(globalDumpedSamplesList[sampleAndGenerators.sampleID] === undefined) {
175
- dumpSample(channel, sampleAndGenerators.sample, sampleAndGenerators.sampleID, sampleDumpCallback);
176
- }
177
-
178
- // can't cache the voice as the end in workletSample maybe is incorrect (the sample is still loading)
179
- if(globalDumpedSamplesList[sampleAndGenerators.sampleID] !== true)
180
- {
181
- canCache = false;
182
- }
183
- }
184
-
185
- // create the generator list
186
- const generators = new Int16Array(60);
187
- // apply and sum the gens
188
- for (let i = 0; i < 60; i++) {
189
- generators[i] = addAndClampGenerator(i, sampleAndGenerators.presetGenerators, sampleAndGenerators.instrumentGenerators);
190
- }
191
-
192
- // !! EMU initial attenuation correction, multiply initial attenuation by 0.4
193
- generators[generatorTypes.initialAttenuation] = Math.floor(generators[generatorTypes.initialAttenuation] * 0.4);
194
-
195
- // key override
196
- let rootKey = sampleAndGenerators.sample.samplePitch;
197
- if (generators[generatorTypes.overridingRootKey] > -1) {
198
- rootKey = generators[generatorTypes.overridingRootKey];
199
- }
200
-
201
- let targetKey = midiNote;
202
- if (generators[generatorTypes.keyNum] > -1) {
203
- targetKey = generators[generatorTypes.keyNum];
204
- }
205
-
206
- // determine looping mode now. if the loop is too small, disable
207
- const loopStart = (sampleAndGenerators.sample.sampleLoopStartIndex / 2) + (generators[generatorTypes.startloopAddrsOffset] + (generators[generatorTypes.startloopAddrsCoarseOffset] * 32768));
208
- const loopEnd = (sampleAndGenerators.sample.sampleLoopEndIndex / 2) + (generators[generatorTypes.endloopAddrsOffset] + (generators[generatorTypes.endloopAddrsCoarseOffset] * 32768));
209
- let loopingMode = generators[generatorTypes.sampleModes];
210
- if (loopEnd - loopStart < 1) {
211
- loopingMode = 0;
212
- }
213
-
214
- // determine end
215
- /**
216
- * create the worklet sample
217
- * @type {WorkletSample}
218
- */
219
- const workletSample = {
220
- sampleID: sampleAndGenerators.sampleID,
221
- playbackStep: (sampleAndGenerators.sample.sampleRate / sampleRate) * Math.pow(2, sampleAndGenerators.sample.samplePitchCorrection / 1200),// cent tuning
222
- cursor: generators[generatorTypes.startAddrsOffset] + (generators[generatorTypes.startAddrsCoarseOffset] * 32768),
223
- rootKey: rootKey,
224
- loopStart: loopStart,
225
- loopEnd: loopEnd,
226
- end: Math.floor( sampleAndGenerators.sample.sampleData.length) - 1 + (generators[generatorTypes.endAddrOffset] + (generators[generatorTypes.endAddrsCoarseOffset] * 32768)),
227
- loopingMode: loopingMode
228
- };
229
-
230
- // velocity override
231
- if (generators[generatorTypes.velocity] > -1) {
232
- velocity = generators[generatorTypes.velocity];
233
- }
234
-
235
- if(debug)
236
- {
237
- SpessaSynthTable([{
238
- Sample: sampleAndGenerators.sample.sampleName,
239
- Generators: generators,
240
- Modulators: sampleAndGenerators.modulators.map(m => m.debugString()),
241
- Velocity: velocity,
242
- TargetKey: targetKey,
243
- MidiNote: midiNote,
244
- WorkletSample: workletSample
245
- }]);
246
- }
247
-
248
- return {
249
- filter: deepClone(DEFAULT_WORKLET_LOWPASS_FILTER),
250
- // generators and modulators
251
- generators: generators,
252
- modulators: sampleAndGenerators.modulators,
253
- modulatedGenerators: new Int16Array(60),
254
-
255
- // sample and playback data
256
- sample: workletSample,
257
- velocity: velocity,
258
- midiNote: midiNote,
259
- channelNumber: channel,
260
- startTime: currentTime,
261
- targetKey: targetKey,
262
- currentTuningCalculated: 1,
263
- currentTuningCents: 0,
264
- releaseStartTime: Infinity,
265
-
266
- // envelope data
267
- finished: false,
268
- isInRelease: false,
269
- hasStarted: false,
270
- currentModEnvValue: 0,
271
- releaseStartModEnv: 1,
272
- currentPan: 0.5,
273
-
274
- volumeEnvelope: deepClone(DEFAULT_WORKLET_VOLUME_ENVELOPE)
275
- };
276
-
277
- });
278
- // cache the voice
279
- if(canCache) {
280
- // clone it so the system won't mess with it!
281
- cachedVoices[midiNote][velocity] = workletVoices.map(deepClone);
282
- }
283
- }
284
- return workletVoices;
285
- }
@@ -1,70 +0,0 @@
1
- /**
2
- *
3
- * @param sampleRate {number}
4
- * @param left {Float32Array}
5
- * @param right {Float32Array}
6
- * @returns {ArrayBufferLike}
7
- */
8
- export function rawDataToWav(sampleRate, left, right)
9
- {
10
-
11
- const length = left.length;
12
-
13
- const bytesPerSample = 2; // 16-bit PCM
14
-
15
- // Prepare the header
16
- const headerSize = 44;
17
- const dataSize = length * 2 * bytesPerSample; // 2 channels, 16-bit per channel
18
- const fileSize = headerSize + dataSize - 8; // total file size minus the first 8 bytes
19
- const header = new Uint8Array(headerSize);
20
-
21
- // 'RIFF'
22
- header.set([82, 73, 70, 70], 0);
23
- // file length
24
- header.set(new Uint8Array([fileSize & 0xff, (fileSize >> 8) & 0xff, (fileSize >> 16) & 0xff, (fileSize >> 24) & 0xff]), 4);
25
- // 'WAVE'
26
- header.set([87, 65, 86, 69], 8);
27
- // 'fmt '
28
- header.set([102, 109, 116, 32], 12);
29
- // fmt chunk length
30
- header.set([16, 0, 0, 0], 16); // 16 for PCM
31
- // audio format (PCM)
32
- header.set([1, 0], 20);
33
- // number of channels (2)
34
- header.set([2, 0], 22);
35
- // sample rate
36
- header.set(new Uint8Array([sampleRate & 0xff, (sampleRate >> 8) & 0xff, (sampleRate >> 16) & 0xff, (sampleRate >> 24) & 0xff]), 24);
37
- // byte rate (sample rate * block align)
38
- const byteRate = sampleRate * 2 * bytesPerSample; // 2 channels, 16-bit per channel
39
- header.set(new Uint8Array([byteRate & 0xff, (byteRate >> 8) & 0xff, (byteRate >> 16) & 0xff, (byteRate >> 24) & 0xff]), 28);
40
- // block align (channels * bytes per sample)
41
- header.set([4, 0], 32); // 2 channels * 16-bit per channel / 8
42
- // bits per sample
43
- header.set([16, 0], 34); // 16-bit
44
-
45
- // data chunk identifier 'data'
46
- header.set([100, 97, 116, 97], 36);
47
- // data chunk length
48
- header.set(new Uint8Array([dataSize & 0xff, (dataSize >> 8) & 0xff, (dataSize >> 16) & 0xff, (dataSize >> 24) & 0xff]), 40);
49
-
50
- const wavData = new Uint8Array(headerSize + dataSize);
51
- wavData.set(header, 0);
52
-
53
- // Interleave audio data (combine channels)
54
- let offset = headerSize;
55
- for (let i = 0; i < length; i++)
56
- {
57
- // interleave both channels
58
- const sample1 = Math.max(-1, Math.min(1, left[i])) * 0x7FFF;
59
- const sample2 = Math.max(-1, Math.min(1, right[i])) * 0x7FFF;
60
-
61
- // convert to 16-bit
62
- wavData[offset++] = sample1 & 0xff;
63
- wavData[offset++] = (sample1 >> 8) & 0xff;
64
- wavData[offset++] = sample2 & 0xff;
65
- wavData[offset++] = (sample2 >> 8) & 0xff;
66
- }
67
-
68
-
69
- return wavData.buffer;
70
- }
@@ -1,141 +0,0 @@
1
- import { ShiftableByteArray } from './shiftable_array.js'
2
-
3
- /**
4
- * byte_functions.js
5
- * purpose: contains various useful functions for bit manipulation and reading
6
- */
7
-
8
- /**
9
- * Reads as little endian
10
- * @param dataArray {ShiftableByteArray}
11
- * @param bytesAmount {number}
12
- * @returns {number}
13
- */
14
- export function readBytesAsUintLittleEndian(dataArray, bytesAmount){
15
- let out = 0;
16
- for(let i = 0; i < bytesAmount; i++)
17
- {
18
- out |= (readByte(dataArray) << i * 8);
19
- }
20
- // make sure it stays unsigned
21
- return out >>> 0;
22
- }
23
-
24
- /**
25
- * Reads as Big endian
26
- * @param dataArray {ShiftableByteArray}
27
- * @param bytesAmount {number}
28
- * @returns {number}
29
- */
30
- export function readBytesAsUintBigEndian(dataArray, bytesAmount){
31
- let out = 0;
32
- for (let i = 8 * (bytesAmount - 1); i >= 0; i -= 8) {
33
- out |= (readByte(dataArray) << i);
34
- }
35
- return out >>> 0;
36
- }
37
-
38
- /**
39
- * @param dataArray {ShiftableByteArray}
40
- * @returns {number}
41
- */
42
- export function readByte(dataArray){
43
- if(!dataArray.shift)
44
- {
45
- dataArray.currentIndex = 0;
46
- dataArray.shift = function () {
47
- this.currentIndex++;
48
- return this[this.currentIndex - 1];
49
- }
50
- }
51
- return dataArray.shift();
52
-
53
- }
54
-
55
- /**
56
- * @param dataArray {ShiftableByteArray}
57
- * @param bytes {number}
58
- * @param encoding {string} the textElement encoding
59
- * @param trimEnd {boolean} if we should trim once we reach an invalid byte
60
- * @returns {string}
61
- */
62
- export function readBytesAsString(dataArray, bytes, encoding=undefined, trimEnd=true){
63
- if(!encoding) {
64
- let finished = false;
65
- let string = "";
66
- for (let i = 0; i < bytes; i++) {
67
- let byte = readByte(dataArray);
68
- if(byte < 32 || byte > 127 || finished)
69
- {
70
- if(trimEnd) {
71
- finished = true;
72
- continue;
73
- }
74
- else
75
- {
76
- if(byte === 0)
77
- {
78
- finished = true;
79
- continue;
80
- }
81
- }
82
- }
83
- string += String.fromCharCode(byte);
84
- }
85
- return string;
86
- }
87
- else {
88
- let byteBuffer = dataArray.slice(dataArray.currentIndex, dataArray.currentIndex + bytes)
89
- dataArray.currentIndex += bytes;
90
- let decoder = new TextDecoder(encoding);
91
- return decoder.decode(byteBuffer.buffer);
92
- }
93
- }
94
-
95
- /**
96
- * @param byte1 {number}
97
- * @param byte2 {number}
98
- * @returns {number}
99
- */
100
- export function signedInt16(byte1, byte2){
101
- let val = (byte2 << 8) | byte1;
102
- if(val > 32767)
103
- {
104
- return val - 65536;
105
- }
106
- return val;
107
- }
108
-
109
- /**
110
- * @param byte {number}
111
- * @returns {number}
112
- */
113
- export function signedInt8(byte) {
114
- if(byte > 127)
115
- {
116
- return byte - 256;
117
- }
118
- return byte;
119
- }
120
-
121
- /**
122
- * Reads VLQ From a MIDI byte array
123
- * @param MIDIbyteArray {ShiftableByteArray}
124
- * @returns {number}
125
- */
126
- export function readVariableLengthQuantity(MIDIbyteArray){
127
- let out = 0;
128
- while(MIDIbyteArray)
129
- {
130
- const byte = readByte(MIDIbyteArray);
131
- // extract the first 7 bytes
132
- out = (out << 7) | (byte & 127);
133
-
134
- // if the last byte isn't 1, stop reading
135
- if((byte >> 7) !== 1)
136
- {
137
- break;
138
- }
139
- }
140
- return out;
141
- }
@@ -1,49 +0,0 @@
1
- /**
2
- * other.js
3
- * purpose: contains some useful functions that don't belong in any specific category
4
- */
5
-
6
- /**
7
- * Formats the given seconds to nice readable time
8
- * @param totalSeconds {number} time in seconds
9
- * @return {{seconds: number, minutes: number, time: string}}
10
- */
11
- export function formatTime(totalSeconds) {
12
- let minutes = Math.floor(totalSeconds / 60);
13
- let seconds = Math.round(totalSeconds - (minutes * 60));
14
- return {"minutes": minutes, "seconds": seconds, "time": `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`}
15
- }
16
-
17
- /**
18
- * @param fileName {string}
19
- * @returns {string}
20
- */
21
- export function formatTitle(fileName)
22
- {
23
- return fileName.replaceAll(".mid", "").replaceAll("_", " ");
24
- }
25
-
26
- /**
27
- * Does what it says
28
- * @param arr {number[]}
29
- * @returns {string}
30
- */
31
- export function arrayToHexString(arr) {
32
- let hexString = '';
33
-
34
- for (let i = 0; i < arr.length; i++) {
35
- const hex = arr[i].toString(16).padStart(2, '0').toUpperCase();
36
- hexString += hex;
37
- hexString += ' ';
38
- }
39
-
40
- return hexString;
41
- }
42
-
43
- export const consoleColors = {
44
- warn: "color: orange;",
45
- unrecognized: "color: red;",
46
- info: "color: aqua;",
47
- recognized: "color: lime",
48
- value: "color: yellow; background-color: black;"
49
- }
@@ -1,26 +0,0 @@
1
- /**
2
- * shiftable_array.js
3
- * purpose: exteds Uint8Array with the Array.shift() function
4
- */
5
-
6
- export class ShiftableByteArray extends Uint8Array
7
- {
8
- /**
9
- * Creates a new instance of an Uint8Array with Array.shift() function
10
- * @param args {any} same as for Uint8Array
11
- */
12
- constructor(args) {
13
- super(args);
14
- this.currentIndex = 0;
15
- }
16
-
17
- /**
18
- * @param amount {number} - Amount of bytes to shift
19
- * @returns {number} - The shifted byte
20
- */
21
- shift = (amount = 1) =>
22
- {
23
- this.currentIndex += amount;
24
- return this[this.currentIndex - amount];
25
- };
26
- }