spessasynth_core 1.1.2 → 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 -303
  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 -222
  177. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/lowpass_filter.js +0 -95
  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 -194
  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 -173
  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 -313
  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,102 +1,96 @@
1
- import {readBytesAsUintLittleEndian} from "../../utils/byte_functions.js";
2
- import {ShiftableByteArray} from "../../utils/shiftable_array.js";
3
- import {RiffChunk} from "./riff_chunk.js";
4
- import {Generator, generatorTypes} from "./generators.js";
5
- import {Sample} from "./samples.js";
6
- import {Instrument} from "./instruments.js";
7
- import {Modulator} from "./modulators.js";
1
+ import { readLittleEndian } from "../../utils/byte_functions/little_endian.js";
2
+ import { IndexedByteArray } from "../../utils/indexed_array.js";
3
+ import { RiffChunk } from "../basic_soundfont/riff_chunk.js";
4
+ import { BasicInstrumentZone, BasicPresetZone } from "../basic_soundfont/basic_zones.js";
5
+ import { Generator, generatorTypes } from "../basic_soundfont/generator.js";
6
+ import { Modulator } from "../basic_soundfont/modulator.js";
8
7
 
9
8
  /**
10
9
  * zones.js
11
10
  * purpose: reads instrumend and preset zones from soundfont and gets their respective samples and generators and modulators
12
11
  */
13
12
 
14
- export class InstrumentZone {
13
+ export class InstrumentZone extends BasicInstrumentZone
14
+ {
15
15
  /**
16
16
  * Creates a zone (instrument)
17
- * @param dataArray {ShiftableByteArray}
17
+ * @param dataArray {IndexedByteArray}
18
18
  */
19
- constructor(dataArray) {
20
- this.generatorZoneStartIndex = readBytesAsUintLittleEndian(dataArray, 2);
21
- this.modulatorZoneStartIndex = readBytesAsUintLittleEndian(dataArray, 2);
19
+ constructor(dataArray)
20
+ {
21
+ super();
22
+ this.generatorZoneStartIndex = readLittleEndian(dataArray, 2);
23
+ this.modulatorZoneStartIndex = readLittleEndian(dataArray, 2);
22
24
  this.modulatorZoneSize = 0;
23
25
  this.generatorZoneSize = 0;
24
- this.keyRange = {min: 0, max: 127};
25
- this.velRange = {min: 0, max: 127}
26
26
  this.isGlobal = true;
27
- /**
28
- * @type {Generator[]}
29
- */
30
- this.generators = [];
31
- /**
32
- * @type {Modulator[]}
33
- */
34
- this.modulators = [];
35
27
  }
36
-
28
+
37
29
  setZoneSize(modulatorZoneSize, generatorZoneSize)
38
30
  {
39
31
  this.modulatorZoneSize = modulatorZoneSize;
40
32
  this.generatorZoneSize = generatorZoneSize;
41
33
  }
42
-
34
+
43
35
  /**
44
36
  * grab the generators
45
37
  * @param generators {Generator[]}
46
38
  */
47
39
  getGenerators(generators)
48
40
  {
49
- for(let i = this.generatorZoneStartIndex; i < this.generatorZoneStartIndex + this.generatorZoneSize; i++)
41
+ for (let i = this.generatorZoneStartIndex; i < this.generatorZoneStartIndex + this.generatorZoneSize; i++)
50
42
  {
51
43
  this.generators.push(generators[i]);
52
44
  }
53
45
  }
54
-
46
+
55
47
  /**
56
48
  * grab the modulators
57
49
  * @param modulators {Modulator[]}
58
50
  */
59
51
  getModulators(modulators)
60
52
  {
61
- for(let i = this.modulatorZoneStartIndex; i < this.modulatorZoneStartIndex + this.modulatorZoneSize; i++)
53
+ for (let i = this.modulatorZoneStartIndex; i < this.modulatorZoneStartIndex + this.modulatorZoneSize; i++)
62
54
  {
63
55
  this.modulators.push(modulators[i]);
64
56
  }
65
57
  }
66
-
58
+
67
59
  /**
68
60
  * Loads the zone's sample
69
- * @param samples {Sample[]}
61
+ * @param samples {BasicSample[]}
70
62
  */
71
- getSample(samples) {
63
+ getSample(samples)
64
+ {
72
65
  let sampleID = this.generators.find(g => g.generatorType === generatorTypes.sampleID);
73
66
  if (sampleID)
74
67
  {
75
68
  this.sample = samples[sampleID.generatorValue];
76
69
  this.isGlobal = false;
70
+ this.sample.useCount++;
77
71
  }
78
72
  }
79
-
73
+
80
74
  /**
81
75
  * Reads the keyRange of the zone
82
76
  */
83
77
  getKeyRange()
84
78
  {
85
79
  let range = this.generators.find(g => g.generatorType === generatorTypes.keyRange);
86
- if(range)
80
+ if (range)
87
81
  {
88
82
  this.keyRange.min = range.generatorValue & 0x7F;
89
83
  this.keyRange.max = (range.generatorValue >> 8) & 0x7F;
90
84
  }
91
85
  }
92
-
86
+
93
87
  /**
94
88
  * reads the velolicty range of the zone
95
89
  */
96
90
  getVelRange()
97
91
  {
98
92
  let range = this.generators.find(g => g.generatorType === generatorTypes.velRange);
99
- if(range)
93
+ if (range)
100
94
  {
101
95
  this.velRange.min = range.generatorValue & 0x7F;
102
96
  this.velRange.max = (range.generatorValue >> 8) & 0x7F;
@@ -105,11 +99,11 @@ export class InstrumentZone {
105
99
  }
106
100
 
107
101
  /**
108
- * Reads the given instrument zone chunk
102
+ * Reads the given instrument zone read
109
103
  * @param zonesChunk {RiffChunk}
110
104
  * @param instrumentGenerators {Generator[]}
111
105
  * @param instrumentModulators {Modulator[]}
112
- * @param instrumentSamples {Sample[]}
106
+ * @param instrumentSamples {BasicSample[]}
113
107
  * @returns {InstrumentZone[]}
114
108
  */
115
109
  export function readInstrumentZones(zonesChunk, instrumentGenerators, instrumentModulators, instrumentSamples)
@@ -118,10 +112,10 @@ export function readInstrumentZones(zonesChunk, instrumentGenerators, instrument
118
112
  * @type {InstrumentZone[]}
119
113
  */
120
114
  let zones = [];
121
- while(zonesChunk.chunkData.length > zonesChunk.chunkData.currentIndex)
115
+ while (zonesChunk.chunkData.length > zonesChunk.chunkData.currentIndex)
122
116
  {
123
117
  let zone = new InstrumentZone(zonesChunk.chunkData);
124
- if(zones.length > 0)
118
+ if (zones.length > 0)
125
119
  {
126
120
  let modulatorZoneSize = zone.modulatorZoneStartIndex - zones[zones.length - 1].modulatorZoneStartIndex;
127
121
  let generatorZoneSize = zone.generatorZoneStartIndex - zones[zones.length - 1].generatorZoneStartIndex;
@@ -134,95 +128,95 @@ export function readInstrumentZones(zonesChunk, instrumentGenerators, instrument
134
128
  }
135
129
  zones.push(zone);
136
130
  }
131
+ if (zones.length > 1)
132
+ {
133
+ // remove terminal
134
+ zones.pop();
135
+ }
137
136
  return zones;
138
137
  }
139
138
 
140
- export class PresetZone {
139
+ export class PresetZone extends BasicPresetZone
140
+ {
141
141
  /**
142
142
  * Creates a zone (preset)
143
- * @param dataArray {ShiftableByteArray}
143
+ * @param dataArray {IndexedByteArray}
144
144
  */
145
- constructor(dataArray) {
146
- this.generatorZoneStartIndex = readBytesAsUintLittleEndian(dataArray, 2);
147
- this.modulatorZoneStartIndex = readBytesAsUintLittleEndian(dataArray, 2);
145
+ constructor(dataArray)
146
+ {
147
+ super();
148
+ this.generatorZoneStartIndex = readLittleEndian(dataArray, 2);
149
+ this.modulatorZoneStartIndex = readLittleEndian(dataArray, 2);
148
150
  this.modulatorZoneSize = 0;
149
151
  this.generatorZoneSize = 0;
150
- this.keyRange = {min: 0, max: 127};
151
- this.velRange = {min: 0, max: 127}
152
152
  this.isGlobal = true;
153
- /**
154
- * @type {Generator[]}
155
- */
156
- this.generators = [];
157
- /**
158
- * @type {Modulator[]}
159
- */
160
- this.modulators = [];
161
153
  }
162
-
154
+
163
155
  setZoneSize(modulatorZoneSize, generatorZoneSize)
164
156
  {
165
157
  this.modulatorZoneSize = modulatorZoneSize;
166
158
  this.generatorZoneSize = generatorZoneSize;
167
159
  }
168
-
160
+
169
161
  /**
170
162
  * grab the generators
171
163
  * @param generators {Generator[]}
172
164
  */
173
165
  getGenerators(generators)
174
166
  {
175
- for(let i = this.generatorZoneStartIndex; i < this.generatorZoneStartIndex + this.generatorZoneSize; i++)
167
+ for (let i = this.generatorZoneStartIndex; i < this.generatorZoneStartIndex + this.generatorZoneSize; i++)
176
168
  {
177
169
  this.generators.push(generators[i]);
178
170
  }
179
171
  }
180
-
172
+
181
173
  /**
182
174
  * grab the modulators
183
175
  * @param modulators {Modulator[]}
184
176
  */
185
177
  getModulators(modulators)
186
178
  {
187
- for(let i = this.modulatorZoneStartIndex; i < this.modulatorZoneStartIndex + this.modulatorZoneSize; i++)
179
+ for (let i = this.modulatorZoneStartIndex; i < this.modulatorZoneStartIndex + this.modulatorZoneSize; i++)
188
180
  {
189
181
  this.modulators.push(modulators[i]);
190
182
  }
191
183
  }
192
-
184
+
193
185
  /**
194
186
  * grab the instrument
195
- * @param instruments {Instrument[]}
187
+ * @param instruments {BasicInstrument[]}
196
188
  */
197
189
  getInstrument(instruments)
198
190
  {
199
191
  let instrumentID = this.generators.find(g => g.generatorType === generatorTypes.instrument);
200
- if(instrumentID) {
192
+ if (instrumentID)
193
+ {
201
194
  this.instrument = instruments[instrumentID.generatorValue];
195
+ this.instrument.addUseCount();
202
196
  this.isGlobal = false;
203
197
  }
204
198
  }
205
-
199
+
206
200
  /**
207
201
  * Reads the keyRange of the zone
208
202
  */
209
203
  getKeyRange()
210
204
  {
211
205
  let range = this.generators.find(g => g.generatorType === generatorTypes.keyRange);
212
- if(range)
206
+ if (range)
213
207
  {
214
208
  this.keyRange.min = range.generatorValue & 0x7F;
215
209
  this.keyRange.max = (range.generatorValue >> 8) & 0x7F;
216
210
  }
217
211
  }
218
-
212
+
219
213
  /**
220
214
  * reads the velolicty range of the zone
221
215
  */
222
216
  getVelRange()
223
217
  {
224
218
  let range = this.generators.find(g => g.generatorType === generatorTypes.velRange);
225
- if(range)
219
+ if (range)
226
220
  {
227
221
  this.velRange.min = range.generatorValue & 0x7F;
228
222
  this.velRange.max = (range.generatorValue >> 8) & 0x7F;
@@ -231,10 +225,10 @@ export class PresetZone {
231
225
  }
232
226
 
233
227
  /**
234
- * Reads the given preset zone chunk
228
+ * Reads the given preset zone read
235
229
  * @param zonesChunk {RiffChunk}
236
230
  * @param presetGenerators {Generator[]}
237
- * @param instruments {Instrument[]}
231
+ * @param instruments {BasicInstrument[]}
238
232
  * @param presetModulators {Modulator[]}
239
233
  * @returns {PresetZone[]}
240
234
  */
@@ -244,10 +238,10 @@ export function readPresetZones(zonesChunk, presetGenerators, presetModulators,
244
238
  * @type {PresetZone[]}
245
239
  */
246
240
  let zones = [];
247
- while(zonesChunk.chunkData.length > zonesChunk.chunkData.currentIndex)
241
+ while (zonesChunk.chunkData.length > zonesChunk.chunkData.currentIndex)
248
242
  {
249
243
  let zone = new PresetZone(zonesChunk.chunkData);
250
- if(zones.length > 0)
244
+ if (zones.length > 0)
251
245
  {
252
246
  let modulatorZoneSize = zone.modulatorZoneStartIndex - zones[zones.length - 1].modulatorZoneStartIndex;
253
247
  let generatorZoneSize = zone.generatorZoneStartIndex - zones[zones.length - 1].generatorZoneStartIndex;
@@ -260,5 +254,10 @@ export function readPresetZones(zonesChunk, presetGenerators, presetModulators,
260
254
  }
261
255
  zones.push(zone);
262
256
  }
257
+ if (zones.length > 1)
258
+ {
259
+ // remove terminal
260
+ zones.pop();
261
+ }
263
262
  return zones;
264
263
  }
@@ -0,0 +1,7 @@
1
+ ## This is the main synthesizer folder.
2
+
3
+ The code here is responsible for making the actual sound.
4
+ This is the heart of the SpessaSynth library.
5
+
6
+ - `audio_engine` - the core synthesis engine, it theoretically can run on non-browser environments.
7
+ - `audio_effects` - the WebAudioAPI audio effects.
@@ -0,0 +1,9 @@
1
+ ## This is the synthesis engine folder.
2
+
3
+ The code here is responsible for a single midi channel, synthesizing the sound to it.
4
+
5
+ - `engine_methods` contains the methods for the `main_processor.js`
6
+ - `engine_components` contains the various digital signal processing functions such as the wavetable oscillator, low
7
+ pass filter, etc.
8
+
9
+ For those interested, `render_voice.js` file contains the actual DSP synthesis code.
@@ -0,0 +1,266 @@
1
+ import { getModulatorCurveValue, MOD_PRECOMPUTED_LENGTH } from "./modulator_curves.js";
2
+ import { VolumeEnvelope } from "./volume_envelope.js";
3
+ import { ModulationEnvelope } from "./modulation_envelope.js";
4
+ import { generatorLimits, generatorTypes } from "../../../soundfont/basic_soundfont/generator.js";
5
+ import { Modulator, modulatorSources } from "../../../soundfont/basic_soundfont/modulator.js";
6
+ import { NON_CC_INDEX_OFFSET } from "./controller_tables.js";
7
+
8
+ /**
9
+ * compute_modulator.js
10
+ * purpose: precomputes all curve types and computes modulators
11
+ */
12
+
13
+ const EFFECT_MODULATOR_TRANSFORM_MULTIPLIER = 1000 / 200;
14
+
15
+ /**
16
+ * Computes a given modulator
17
+ * @param controllerTable {Int16Array} all midi controllers as 14bit values + the non-controller indexes, starting at 128
18
+ * @param modulator {Modulator} the modulator to compute
19
+ * @param voice {Voice} the voice belonging to the modulator
20
+ * @returns {number} the computed value
21
+ */
22
+ export function computeModulator(controllerTable, modulator, voice)
23
+ {
24
+ if (modulator.transformAmount === 0)
25
+ {
26
+ modulator.currentValue = 0;
27
+ return 0;
28
+ }
29
+ // mapped to 0-16384
30
+ let rawSourceValue;
31
+ if (modulator.sourceUsesCC)
32
+ {
33
+ rawSourceValue = controllerTable[modulator.sourceIndex];
34
+ }
35
+ else
36
+ {
37
+ const index = modulator.sourceIndex + NON_CC_INDEX_OFFSET;
38
+ switch (modulator.sourceIndex)
39
+ {
40
+ case modulatorSources.noController:
41
+ rawSourceValue = 16383; // equals to 1
42
+ break;
43
+
44
+ case modulatorSources.noteOnKeyNum:
45
+ rawSourceValue = voice.midiNote << 7;
46
+ break;
47
+
48
+ case modulatorSources.noteOnVelocity:
49
+ rawSourceValue = voice.velocity << 7;
50
+ break;
51
+
52
+ case modulatorSources.polyPressure:
53
+ rawSourceValue = voice.pressure << 7;
54
+ break;
55
+
56
+ default:
57
+ rawSourceValue = controllerTable[index]; // pitch bend and range are stored in the cc table
58
+ break;
59
+ }
60
+
61
+ }
62
+
63
+ const sourceValue = transforms[modulator.sourceCurveType][modulator.sourcePolarity][modulator.sourceDirection][rawSourceValue];
64
+
65
+ // mapped to 0-127
66
+ let rawSecondSrcValue;
67
+ if (modulator.secSrcUsesCC)
68
+ {
69
+ rawSecondSrcValue = controllerTable[modulator.secSrcIndex];
70
+ }
71
+ else
72
+ {
73
+ const index = modulator.secSrcIndex + NON_CC_INDEX_OFFSET;
74
+ switch (modulator.secSrcIndex)
75
+ {
76
+ case modulatorSources.noController:
77
+ rawSecondSrcValue = 16383; // equals to 1
78
+ break;
79
+
80
+ case modulatorSources.noteOnKeyNum:
81
+ rawSecondSrcValue = voice.midiNote << 7;
82
+ break;
83
+
84
+ case modulatorSources.noteOnVelocity:
85
+ rawSecondSrcValue = voice.velocity << 7;
86
+ break;
87
+
88
+ case modulatorSources.polyPressure:
89
+ rawSecondSrcValue = voice.pressure << 7;
90
+ break;
91
+
92
+ default:
93
+ rawSecondSrcValue = controllerTable[index]; // pitch bend and range are stored in the cc table
94
+ }
95
+
96
+ }
97
+ const secondSrcValue = transforms[modulator.secSrcCurveType][modulator.secSrcPolarity][modulator.secSrcDirection][rawSecondSrcValue];
98
+
99
+ // see the comment for isEffectModulator (modulator.js in basic_soundfont) for explanation
100
+ let transformAmount = modulator.transformAmount;
101
+ if (modulator.isEffectModulator && transformAmount <= 1000)
102
+ {
103
+ transformAmount *= EFFECT_MODULATOR_TRANSFORM_MULTIPLIER;
104
+ transformAmount = Math.min(transformAmount, 1000);
105
+ }
106
+
107
+ // compute the modulator
108
+ let computedValue = sourceValue * secondSrcValue * transformAmount;
109
+
110
+ if (modulator.transformType === 2)
111
+ {
112
+ // abs value
113
+ computedValue = Math.abs(computedValue);
114
+ }
115
+
116
+ modulator.currentValue = computedValue;
117
+ return computedValue;
118
+ }
119
+
120
+ /**
121
+ * Computes modulators of a given voice. Source and index indicate what modulators shall be computed
122
+ * @param voice {Voice} the voice to compute modulators for
123
+ * @param controllerTable {Int16Array} all midi controllers as 14bit values + the non-controller indexes, starting at 128
124
+ * @param sourceUsesCC {number} what modulators should be computed, -1 means all, 0 means modulator source enum 1 means midi controller
125
+ * @param sourceIndex {number} enum for the source
126
+ */
127
+ export function computeModulators(voice, controllerTable, sourceUsesCC = -1, sourceIndex = 0)
128
+ {
129
+ const modulators = voice.modulators;
130
+ const generators = voice.generators;
131
+ const modulatedGenerators = voice.modulatedGenerators;
132
+
133
+ if (sourceUsesCC === -1)
134
+ {
135
+ // All modulators mode: compute all modulators
136
+ modulatedGenerators.set(generators);
137
+ modulators.forEach(mod =>
138
+ {
139
+ const limits = generatorLimits[mod.modulatorDestination];
140
+ const newValue = modulatedGenerators[mod.modulatorDestination] + computeModulator(
141
+ controllerTable,
142
+ mod,
143
+ voice
144
+ );
145
+ modulatedGenerators[mod.modulatorDestination] = Math.max(
146
+ limits.min,
147
+ Math.min(newValue, limits.max)
148
+ );
149
+ });
150
+ VolumeEnvelope.recalculate(voice);
151
+ ModulationEnvelope.recalculate(voice);
152
+ return;
153
+ }
154
+
155
+ // Optimized mode: calculate only modulators that use the given source
156
+ const volenvNeedsRecalculation = new Set([
157
+ generatorTypes.initialAttenuation,
158
+ generatorTypes.delayVolEnv,
159
+ generatorTypes.attackVolEnv,
160
+ generatorTypes.holdVolEnv,
161
+ generatorTypes.decayVolEnv,
162
+ generatorTypes.sustainVolEnv,
163
+ generatorTypes.releaseVolEnv,
164
+ generatorTypes.keyNumToVolEnvHold,
165
+ generatorTypes.keyNumToVolEnvDecay
166
+ ]);
167
+
168
+ const computedDestinations = new Set();
169
+
170
+ modulators.forEach(mod =>
171
+ {
172
+ if (
173
+ (mod.sourceUsesCC === sourceUsesCC && mod.sourceIndex === sourceIndex) ||
174
+ (mod.secSrcUsesCC === sourceUsesCC && mod.secSrcIndex === sourceIndex)
175
+ )
176
+ {
177
+ const destination = mod.modulatorDestination;
178
+ if (!computedDestinations.has(destination))
179
+ {
180
+ // Reset this destination
181
+ modulatedGenerators[destination] = generators[destination];
182
+ // compute our modulator
183
+ computeModulator(controllerTable, mod, voice);
184
+ // sum the values of all modulators for this destination
185
+ modulators.forEach(m =>
186
+ {
187
+ if (m.modulatorDestination === destination)
188
+ {
189
+ const limits = generatorLimits[mod.modulatorDestination];
190
+ const newValue = modulatedGenerators[mod.modulatorDestination] + m.currentValue;
191
+ modulatedGenerators[mod.modulatorDestination] = Math.max(
192
+ limits.min,
193
+ Math.min(newValue, limits.max)
194
+ );
195
+ }
196
+ });
197
+ computedDestinations.add(destination);
198
+ }
199
+ }
200
+ });
201
+
202
+ // Recalculate volume envelope if necessary
203
+ if ([...computedDestinations].some(dest => volenvNeedsRecalculation.has(dest)))
204
+ {
205
+ VolumeEnvelope.recalculate(voice);
206
+ }
207
+
208
+ ModulationEnvelope.recalculate(voice);
209
+ }
210
+
211
+
212
+ /**
213
+ * as follows: transforms[curveType][polarity][direction] is an array
214
+ * @type {Float32Array[][][]}
215
+ */
216
+ const transforms = [];
217
+
218
+ for (let curve = 0; curve < 4; curve++)
219
+ {
220
+ transforms[curve] =
221
+ [
222
+ [
223
+ new Float32Array(MOD_PRECOMPUTED_LENGTH),
224
+ new Float32Array(MOD_PRECOMPUTED_LENGTH)
225
+ ],
226
+ [
227
+ new Float32Array(MOD_PRECOMPUTED_LENGTH),
228
+ new Float32Array(MOD_PRECOMPUTED_LENGTH)
229
+ ]
230
+ ];
231
+ for (let i = 0; i < MOD_PRECOMPUTED_LENGTH; i++)
232
+ {
233
+
234
+ // polarity 0 dir 0
235
+ transforms[curve][0][0][i] = getModulatorCurveValue(
236
+ 0,
237
+ curve,
238
+ i / MOD_PRECOMPUTED_LENGTH,
239
+ 0
240
+ );
241
+
242
+ // polarity 1 dir 0
243
+ transforms[curve][1][0][i] = getModulatorCurveValue(
244
+ 0,
245
+ curve,
246
+ i / MOD_PRECOMPUTED_LENGTH,
247
+ 1
248
+ );
249
+
250
+ // polarity 0 dir 1
251
+ transforms[curve][0][1][i] = getModulatorCurveValue(
252
+ 1,
253
+ curve,
254
+ i / MOD_PRECOMPUTED_LENGTH,
255
+ 0
256
+ );
257
+
258
+ // polarity 1 dir 1
259
+ transforms[curve][1][1][i] = getModulatorCurveValue(
260
+ 1,
261
+ curve,
262
+ i / MOD_PRECOMPUTED_LENGTH,
263
+ 1
264
+ );
265
+ }
266
+ }
@@ -0,0 +1,88 @@
1
+ import { midiControllers } from "../../../midi/midi_message.js";
2
+ import { modulatorSources } from "../../../soundfont/basic_soundfont/modulator.js";
3
+
4
+ /*
5
+ * A bit of explanation:
6
+ * The controller table is stored as an int16 array, it stores 14-bit values.
7
+ * This controller table is then extended with the modulatorSources section,
8
+ * for example, pitch range and pitch range depth.
9
+ * This allows us for precise control range and supports full pitch-wheel resolution.
10
+ */
11
+ export const NON_CC_INDEX_OFFSET = 128;
12
+ export const CONTROLLER_TABLE_SIZE = 147;
13
+
14
+
15
+ // an array with preset default values, so we can quickly use set() to reset the controllers
16
+ export const resetArray = new Int16Array(CONTROLLER_TABLE_SIZE).fill(0);
17
+ export const setResetValue = (i, v) => resetArray[i] = v << 7;
18
+
19
+ // values come from Falcosoft MidiPlayer 6
20
+ setResetValue(midiControllers.mainVolume, 100);
21
+ setResetValue(midiControllers.balance, 64);
22
+ setResetValue(midiControllers.expressionController, 127);
23
+ setResetValue(midiControllers.pan, 64);
24
+
25
+ setResetValue(midiControllers.portamentoOnOff, 127);
26
+
27
+ setResetValue(midiControllers.filterResonance, 64);
28
+ setResetValue(midiControllers.releaseTime, 64);
29
+ setResetValue(midiControllers.attackTime, 64);
30
+ setResetValue(midiControllers.brightness, 64);
31
+
32
+ setResetValue(midiControllers.decayTime, 64);
33
+ setResetValue(midiControllers.vibratoRate, 64);
34
+ setResetValue(midiControllers.vibratoDepth, 64);
35
+ setResetValue(midiControllers.vibratoDelay, 64);
36
+ setResetValue(midiControllers.generalPurposeController6, 64);
37
+ setResetValue(midiControllers.generalPurposeController8, 64);
38
+
39
+ setResetValue(midiControllers.RPNLsb, 127);
40
+ setResetValue(midiControllers.RPNMsb, 127);
41
+ setResetValue(midiControllers.NRPNLsb, 127);
42
+ setResetValue(midiControllers.NRPNMsb, 127);
43
+
44
+
45
+ export const PORTAMENTO_CONTROL_UNSET = 1;
46
+ // special case: portamento control
47
+ // since it is only 7-bit, only the values at multiple of 128 are allowed.
48
+ // a value of just 1 indicates no key set, hence no portamento.
49
+ // this is the "initial unset portamento key" flag.
50
+ resetArray[midiControllers.portamentoControl] = PORTAMENTO_CONTROL_UNSET;
51
+
52
+ // pitch wheel
53
+ setResetValue(NON_CC_INDEX_OFFSET + modulatorSources.pitchWheel, 64);
54
+ setResetValue(NON_CC_INDEX_OFFSET + modulatorSources.pitchWheelRange, 2);
55
+
56
+ /**
57
+ * @enum {number}
58
+ */
59
+ export const customControllers = {
60
+ channelTuning: 0, // cents, RPN for fine tuning
61
+ channelTransposeFine: 1, // cents, only the decimal tuning, (e.g., transpose is 4.5,
62
+ // then shift by 4 keys + tune by 50 cents)
63
+ modulationMultiplier: 2, // cents, set by modulation depth RPN
64
+ masterTuning: 3, // cents, set by system exclusive
65
+ channelTuningSemitones: 4 // semitones, for RPN coarse tuning
66
+ };
67
+ export const CUSTOM_CONTROLLER_TABLE_SIZE = Object.keys(customControllers).length;
68
+ export const customResetArray = new Float32Array(CUSTOM_CONTROLLER_TABLE_SIZE);
69
+ customResetArray[customControllers.modulationMultiplier] = 1;
70
+ /**
71
+ * @enum {number}
72
+ */
73
+ export const dataEntryStates = {
74
+ Idle: 0,
75
+ RPCoarse: 1,
76
+ RPFine: 2,
77
+ NRPCoarse: 3,
78
+ NRPFine: 4,
79
+ DataCoarse: 5,
80
+ DataFine: 6
81
+ };
82
+ /**
83
+ * This is a channel configuration enum, it is internally sent from Synthetizer via controller change
84
+ * @enum {number}
85
+ */
86
+ export const channelConfiguration = {
87
+ velocityOverride: 128 // overrides velocity for the given channel
88
+ };