spessasynth_lib 3.25.21 → 3.25.22

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 (80) hide show
  1. package/index.js +7 -7
  2. package/{midi_parser → midi}/basic_midi.js +6 -4
  3. package/{midi_parser → midi}/midi_loader.js +1 -1
  4. package/midi/midi_tools/get_note_times.js +154 -0
  5. package/{midi_parser → midi/midi_tools}/midi_editor.js +8 -8
  6. package/{midi_parser → midi/midi_tools}/midi_writer.js +3 -3
  7. package/{midi_parser → midi/midi_tools}/rmidi_writer.js +10 -10
  8. package/{midi_parser → midi/midi_tools}/used_keys_loaded.js +7 -7
  9. package/{midi_parser → midi}/xmf_loader.js +1 -1
  10. package/package.json +1 -1
  11. package/sequencer/sequencer_engine/events.js +1 -1
  12. package/sequencer/sequencer_engine/play.js +3 -5
  13. package/sequencer/sequencer_engine/process_event.js +1 -1
  14. package/sequencer/sequencer_engine/sequencer_engine.js +1 -1
  15. package/sequencer/sequencer_engine/song_control.js +3 -3
  16. package/sequencer/worklet_wrapper/sequencer.js +3 -3
  17. package/soundfont/basic_soundfont/modulator.js +1 -1
  18. package/soundfont/basic_soundfont/write_dls/modulator_converter.js +1 -1
  19. package/soundfont/dls/articulator_converter.js +1 -1
  20. package/synthetizer/audio_engine/README.md +5 -5
  21. package/synthetizer/audio_engine/{worklet_utilities/worklet_modulator.js → engine_components/compute_modulator.js} +12 -12
  22. package/synthetizer/audio_engine/{worklet_utilities → engine_components}/controller_tables.js +1 -1
  23. package/synthetizer/audio_engine/{worklet_methods/worklet_key_modifier.js → engine_components/key_modifier_manager.js} +24 -32
  24. package/synthetizer/audio_engine/{worklet_utilities → engine_components}/lowpass_filter.js +1 -1
  25. package/synthetizer/audio_engine/{worklet_utilities/worklet_processor_channel.js → engine_components/midi_audio_channel.js} +43 -43
  26. package/synthetizer/audio_engine/{worklet_utilities → engine_components}/modulation_envelope.js +6 -6
  27. package/synthetizer/audio_engine/{worklet_utilities → engine_components}/stereo_panner.js +3 -3
  28. package/synthetizer/audio_engine/{worklet_utilities/worklet_voice.js → engine_components/voice.js} +43 -37
  29. package/synthetizer/audio_engine/{worklet_utilities → engine_components}/volume_envelope.js +17 -17
  30. package/synthetizer/audio_engine/{worklet_utilities → engine_components}/wavetable_oscillator.js +3 -3
  31. package/synthetizer/audio_engine/{worklet_methods → engine_methods}/controller_control/controller_change.js +4 -4
  32. package/synthetizer/audio_engine/engine_methods/controller_control/master_parameters.js +48 -0
  33. package/synthetizer/audio_engine/{worklet_methods → engine_methods}/controller_control/reset_controllers.js +6 -6
  34. package/synthetizer/audio_engine/{worklet_methods/create_worklet_channel.js → engine_methods/create_midi_channel.js} +4 -4
  35. package/synthetizer/audio_engine/{worklet_methods → engine_methods}/data_entry/data_entry_coarse.js +3 -3
  36. package/synthetizer/audio_engine/{worklet_methods → engine_methods}/data_entry/data_entry_fine.js +3 -3
  37. package/synthetizer/audio_engine/{worklet_methods → engine_methods}/mute_channel.js +1 -1
  38. package/synthetizer/audio_engine/{worklet_methods → engine_methods}/note_on.js +10 -4
  39. package/synthetizer/audio_engine/{worklet_methods → engine_methods}/program_change.js +1 -1
  40. package/synthetizer/audio_engine/{worklet_methods → engine_methods}/render_voice.js +13 -13
  41. package/synthetizer/audio_engine/{worklet_methods → engine_methods}/stopping_notes/kill_note.js +1 -1
  42. package/synthetizer/audio_engine/{worklet_methods → engine_methods}/stopping_notes/note_off.js +1 -1
  43. package/synthetizer/audio_engine/{worklet_methods → engine_methods}/stopping_notes/stop_all_notes.js +1 -1
  44. package/synthetizer/audio_engine/{worklet_methods → engine_methods}/stopping_notes/voice_killing.js +2 -2
  45. package/synthetizer/audio_engine/{worklet_methods → engine_methods}/system_exclusive.js +4 -3
  46. package/synthetizer/audio_engine/{worklet_methods → engine_methods}/tuning_control/channel_pressure.js +3 -3
  47. package/synthetizer/audio_engine/{worklet_methods → engine_methods}/tuning_control/pitch_wheel.js +3 -3
  48. package/synthetizer/audio_engine/{worklet_methods → engine_methods}/tuning_control/poly_pressure.js +2 -2
  49. package/synthetizer/audio_engine/{worklet_methods → engine_methods}/tuning_control/set_master_tuning.js +1 -1
  50. package/synthetizer/audio_engine/{worklet_methods → engine_methods}/tuning_control/set_modulation_depth.js +2 -2
  51. package/synthetizer/audio_engine/{worklet_methods → engine_methods}/tuning_control/set_octave_tuning.js +1 -1
  52. package/synthetizer/audio_engine/{worklet_methods → engine_methods}/tuning_control/set_tuning.js +2 -2
  53. package/synthetizer/audio_engine/{worklet_methods → engine_methods}/tuning_control/transpose_channel.js +3 -3
  54. package/synthetizer/audio_engine/main_processor.js +49 -44
  55. package/synthetizer/audio_engine/message_protocol/worklet_message.js +1 -12
  56. package/synthetizer/audio_engine/snapshot/synthesizer_snapshot.js +3 -2
  57. package/synthetizer/worklet_processor.min.js +12 -12
  58. package/synthetizer/worklet_wrapper/key_modifier_manager.js +6 -4
  59. package/synthetizer/worklet_wrapper/synth_soundfont_manager.js +1 -1
  60. package/synthetizer/worklet_wrapper/synthetizer.js +6 -6
  61. package/synthetizer/worklet_wrapper/worklet_processor.js +26 -24
  62. package/synthetizer/audio_engine/worklet_methods/controller_control/master_parameters.js +0 -36
  63. /package/{midi_parser → midi}/README.md +0 -0
  64. /package/{midi_parser → midi}/midi_builder.js +0 -0
  65. /package/{midi_parser → midi}/midi_data.js +0 -0
  66. /package/{midi_parser → midi}/midi_message.js +0 -0
  67. /package/{midi_parser → midi}/midi_sequence.js +0 -0
  68. /package/synthetizer/audio_engine/{worklet_utilities → engine_components}/lfo.js +0 -0
  69. /package/synthetizer/audio_engine/{worklet_utilities → engine_components}/modulator_curves.js +0 -0
  70. /package/synthetizer/audio_engine/{worklet_methods/worklet_soundfont_manager → engine_components/soundfont_manager}/sfman_message.js +0 -0
  71. /package/synthetizer/audio_engine/{worklet_methods/worklet_soundfont_manager/worklet_soundfont_manager.js → engine_components/soundfont_manager/soundfont_manager.js} +0 -0
  72. /package/synthetizer/audio_engine/{worklet_utilities → engine_components}/unit_converter.js +0 -0
  73. /package/synthetizer/audio_engine/{worklet_methods → engine_methods}/portamento_time.js +0 -0
  74. /package/synthetizer/audio_engine/{worklet_methods → engine_methods}/soundfont_management/clear_sound_font.js +0 -0
  75. /package/synthetizer/audio_engine/{worklet_methods → engine_methods}/soundfont_management/get_preset.js +0 -0
  76. /package/synthetizer/audio_engine/{worklet_methods → engine_methods}/soundfont_management/reload_sound_font.js +0 -0
  77. /package/synthetizer/audio_engine/{worklet_methods → engine_methods}/soundfont_management/send_preset_list.js +0 -0
  78. /package/synthetizer/audio_engine/{worklet_methods → engine_methods}/soundfont_management/set_embedded_sound_font.js +0 -0
  79. /package/synthetizer/audio_engine/{worklet_methods → engine_methods}/stopping_notes/stop_all_channels.js +0 -0
  80. /package/synthetizer/audio_engine/{worklet_methods → engine_methods}/tuning_control/transpose_all_channels.js +0 -0
package/index.js CHANGED
@@ -8,18 +8,18 @@ import { Generator } from "./soundfont/basic_soundfont/generator.js";
8
8
  import { Modulator } from "./soundfont/basic_soundfont/modulator.js";
9
9
  import { BasicPresetZone } from "./soundfont/basic_soundfont/basic_zones.js";
10
10
  import { BasicPreset } from "./soundfont/basic_soundfont/basic_preset.js";
11
- import { BasicMIDI } from "./midi_parser/basic_midi.js";
12
- import { MIDIMessage } from "./midi_parser/midi_message.js";
13
- import { MIDI } from './midi_parser/midi_loader.js';
14
- import { RMIDINFOChunks } from "./midi_parser/rmidi_writer.js";
15
- import { MIDIBuilder } from "./midi_parser/midi_builder.js";
11
+ import { BasicMIDI } from "./midi/basic_midi.js";
12
+ import { MIDIMessage } from "./midi/midi_message.js";
13
+ import { MIDI } from "./midi/midi_loader.js";
14
+ import { RMIDINFOChunks } from "./midi/midi_tools/rmidi_writer.js";
15
+ import { MIDIBuilder } from "./midi/midi_builder.js";
16
16
  import { Synthetizer } from "./synthetizer/worklet_wrapper/synthetizer.js";
17
17
  import { VOICE_CAP, DEFAULT_PERCUSSION } from "./synthetizer/synth_constants.js";
18
18
  import { Sequencer } from "./sequencer/worklet_wrapper/sequencer.js";
19
19
  import { IndexedByteArray } from './utils/indexed_array.js';
20
20
  import { audioBufferToWav } from './utils/buffer_to_wav.js';
21
21
  import { SpessaSynthLogging } from './utils/loggin.js';
22
- import { midiControllers, messageTypes } from './midi_parser/midi_message.js';
22
+ import { midiControllers, messageTypes } from "./midi/midi_message.js";
23
23
  import { MIDIDeviceHandler} from "./external_midi/midi_handler.js";
24
24
  import { WebMIDILinkHandler} from "./external_midi/web_midi_link.js";
25
25
  import { modulatorSources } from "./soundfont/basic_soundfont/modulator.js";
@@ -28,7 +28,7 @@ import { WORKLET_URL_ABSOLUTE } from "./synthetizer/worklet_wrapper/worklet_url.
28
28
  import { SynthesizerSnapshot } from "./synthetizer/audio_engine/snapshot/synthesizer_snapshot.js";
29
29
  import { ChannelSnapshot } from "./synthetizer/audio_engine/snapshot/channel_snapshot.js";
30
30
  import { SpessaSynthProcessor } from "./synthetizer/audio_engine/main_processor.js";
31
- import { NON_CC_INDEX_OFFSET } from "./synthetizer/audio_engine/worklet_utilities/controller_tables.js";
31
+ import { NON_CC_INDEX_OFFSET } from "./synthetizer/audio_engine/engine_components/controller_tables.js";
32
32
  import { ALL_CHANNELS_OR_DIFFERENT_ACTION } from "./synthetizer/audio_engine/message_protocol/worklet_message.js";
33
33
 
34
34
  // Export modules
@@ -4,11 +4,12 @@ import { messageTypes, MIDIMessage } from "./midi_message.js";
4
4
  import { readBytesAsUintBigEndian } from "../utils/byte_functions/big_endian.js";
5
5
  import { SpessaSynthGroup, SpessaSynthGroupEnd, SpessaSynthInfo } from "../utils/loggin.js";
6
6
  import { consoleColors, formatTitle, sanitizeKarLyrics } from "../utils/other.js";
7
- import { writeMIDI } from "./midi_writer.js";
8
- import { applySnapshotToMIDI, modifyMIDI } from "./midi_editor.js";
9
- import { writeRMIDI } from "./rmidi_writer.js";
10
- import { getUsedProgramsAndKeys } from "./used_keys_loaded.js";
7
+ import { writeMIDI } from "./midi_tools/midi_writer.js";
8
+ import { applySnapshotToMIDI, modifyMIDI } from "./midi_tools/midi_editor.js";
9
+ import { writeRMIDI } from "./midi_tools/rmidi_writer.js";
10
+ import { getUsedProgramsAndKeys } from "./midi_tools/used_keys_loaded.js";
11
11
  import { IndexedByteArray } from "../utils/indexed_array.js";
12
+ import { getNoteTimes } from "./midi_tools/get_note_times.js";
12
13
 
13
14
  /**
14
15
  * BasicMIDI is the base of a complete MIDI file, used by the sequencer internally.
@@ -559,5 +560,6 @@ BasicMIDI.prototype.modifyMIDI = modifyMIDI;
559
560
  BasicMIDI.prototype.applySnapshotToMIDI = applySnapshotToMIDI;
560
561
  BasicMIDI.prototype.writeRMIDI = writeRMIDI;
561
562
  BasicMIDI.prototype.getUsedProgramsAndKeys = getUsedProgramsAndKeys;
563
+ BasicMIDI.prototype.getNoteTimes = getNoteTimes;
562
564
 
563
565
  export { BasicMIDI };
@@ -7,7 +7,7 @@ import { readVariableLengthQuantity } from "../utils/byte_functions/variable_len
7
7
  import { readBytesAsUintBigEndian } from "../utils/byte_functions/big_endian.js";
8
8
  import { readBytesAsString } from "../utils/byte_functions/string.js";
9
9
  import { readLittleEndian } from "../utils/byte_functions/little_endian.js";
10
- import { RMIDINFOChunks } from "./rmidi_writer.js";
10
+ import { RMIDINFOChunks } from "./midi_tools/rmidi_writer.js";
11
11
  import { BasicMIDI } from "./basic_midi.js";
12
12
  import { loadXMF } from "./xmf_loader.js";
13
13
 
@@ -0,0 +1,154 @@
1
+ import { IndexedByteArray } from "../../utils/indexed_array.js";
2
+ import { readBytesAsUintBigEndian } from "../../utils/byte_functions/big_endian.js";
3
+ import { DEFAULT_PERCUSSION } from "../../synthetizer/synth_constants.js";
4
+
5
+ /**
6
+ * Calculates all note times in seconds.
7
+ * @this {BasicMIDI}
8
+ * @param minDrumLength {number} the shortest a drum note (channel 10) can be, in seconds.
9
+ * @returns {{
10
+ * midiNote: number,
11
+ * start: number,
12
+ * length: number,
13
+ * velocity: number,
14
+ * }[][]} an array of 16 channels, each channel containing its notes,
15
+ * with their key number, velocity, absolute start time and length in seconds.
16
+ */
17
+ export function getNoteTimes(minDrumLength = 0)
18
+ {
19
+
20
+ /**
21
+ * gets tempo from the midi message
22
+ * @param event {MIDIMessage}
23
+ * @return {number} the tempo in bpm
24
+ */
25
+ function getTempo(event)
26
+ {
27
+ // simulate IndexedByteArray
28
+ event.messageData = new IndexedByteArray(event.messageData.buffer);
29
+ event.messageData.currentIndex = 0;
30
+ return 60000000 / readBytesAsUintBigEndian(event.messageData, 3);
31
+ }
32
+
33
+ /**
34
+ * an array of 16 arrays (channels)
35
+ * @type {{
36
+ * midiNote: number,
37
+ * start: number,
38
+ * length: number,
39
+ * velocity: number,
40
+ * }[][]}
41
+ */
42
+ const noteTimes = [];
43
+ // flatten and sort by ticks
44
+ const trackData = this.tracks;
45
+ let events = trackData.flat();
46
+ events.sort((e1, e2) => e1.ticks - e2.ticks);
47
+
48
+ for (let i = 0; i < 16; i++)
49
+ {
50
+ noteTimes.push([]);
51
+ }
52
+ let elapsedTime = 0;
53
+ let oneTickToSeconds = 60 / (120 * this.timeDivision);
54
+ let eventIndex = 0;
55
+ let unfinished = 0;
56
+ /**
57
+ * @type {{
58
+ * midiNote: number,
59
+ * start: number,
60
+ * length: number,
61
+ * velocity: number,
62
+ * }[][]}
63
+ */
64
+ const unfinishedNotes = [];
65
+ for (let i = 0; i < 16; i++)
66
+ {
67
+ unfinishedNotes.push([]);
68
+ }
69
+ const noteOff = (midiNote, channel) =>
70
+ {
71
+ const noteIndex = unfinishedNotes[channel].findIndex(n => n.midiNote === midiNote);
72
+ const note = unfinishedNotes[channel][noteIndex];
73
+ if (note)
74
+ {
75
+ const time = elapsedTime - note.start;
76
+ note.length = time;
77
+ if (channel === DEFAULT_PERCUSSION)
78
+ {
79
+ note.length = time < minDrumLength ? minDrumLength : time;
80
+ }
81
+ // delete from unfinished
82
+ unfinishedNotes[channel].splice(noteIndex, 1);
83
+ }
84
+ unfinished--;
85
+ };
86
+ while (eventIndex < events.length)
87
+ {
88
+ const event = events[eventIndex];
89
+
90
+ const status = event.messageStatusByte >> 4;
91
+ const channel = event.messageStatusByte & 0x0F;
92
+
93
+ // note off
94
+ if (status === 0x8)
95
+ {
96
+ noteOff(event.messageData[0], channel);
97
+ }
98
+ // note on
99
+ else if (status === 0x9)
100
+ {
101
+ if (event.messageData[1] === 0)
102
+ {
103
+ // never mind, its note off
104
+ noteOff(event.messageData[0], channel);
105
+ }
106
+ else
107
+ {
108
+ // stop previous
109
+ noteOff(event.messageData[0], channel);
110
+ const noteTime = {
111
+ midiNote: event.messageData[0],
112
+ start: elapsedTime,
113
+ length: -1,
114
+ velocity: event.messageData[1] / 127
115
+ };
116
+ noteTimes[channel].push(noteTime);
117
+ unfinishedNotes[channel].push(noteTime);
118
+ unfinished++;
119
+
120
+ }
121
+ }
122
+ // set tempo
123
+ else if (event.messageStatusByte === 0x51)
124
+ {
125
+ oneTickToSeconds = 60 / (getTempo(event) * this.timeDivision);
126
+ }
127
+
128
+ if (++eventIndex >= events.length)
129
+ {
130
+ break;
131
+ }
132
+
133
+ elapsedTime += oneTickToSeconds * (events[eventIndex].ticks - event.ticks);
134
+ }
135
+
136
+ // finish the unfinished notes
137
+ if (unfinished > 0)
138
+ {
139
+ // for every channel, for every note that is unfinished (has -1 length)
140
+ unfinishedNotes.forEach((channelNotes, channel) =>
141
+ {
142
+ channelNotes.forEach(note =>
143
+ {
144
+ const time = elapsedTime - note.start;
145
+ note.length = time;
146
+ if (channel === DEFAULT_PERCUSSION)
147
+ {
148
+ note.length = time < minDrumLength ? minDrumLength : time;
149
+ }
150
+ });
151
+ });
152
+ }
153
+ return noteTimes;
154
+ }
@@ -1,12 +1,12 @@
1
- import { messageTypes, midiControllers, MIDIMessage } from "./midi_message.js";
2
- import { IndexedByteArray } from "../utils/indexed_array.js";
3
- import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd, SpessaSynthInfo } from "../utils/loggin.js";
4
- import { consoleColors } from "../utils/other.js";
1
+ import { messageTypes, midiControllers, MIDIMessage } from "../midi_message.js";
2
+ import { IndexedByteArray } from "../../utils/indexed_array.js";
3
+ import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd, SpessaSynthInfo } from "../../utils/loggin.js";
4
+ import { consoleColors } from "../../utils/other.js";
5
5
 
6
- import { customControllers } from "../synthetizer/audio_engine/worklet_utilities/controller_tables.js";
7
- import { DEFAULT_PERCUSSION } from "../synthetizer/synth_constants.js";
8
- import { isGM2On, isGMOn, isGSOn, isXGOn } from "../utils/sysex_detector.js";
9
- import { isSystemXG, isXGDrums, XG_SFX_VOICE } from "../utils/xg_hacks.js";
6
+ import { customControllers } from "../../synthetizer/audio_engine/engine_components/controller_tables.js";
7
+ import { DEFAULT_PERCUSSION } from "../../synthetizer/synth_constants.js";
8
+ import { isGM2On, isGMOn, isGSOn, isXGOn } from "../../utils/sysex_detector.js";
9
+ import { isSystemXG, isXGDrums, XG_SFX_VOICE } from "../../utils/xg_hacks.js";
10
10
 
11
11
  /**
12
12
  * @param ticks {number}
@@ -1,6 +1,6 @@
1
- import { messageTypes } from "./midi_message.js";
2
- import { writeVariableLengthQuantity } from "../utils/byte_functions/variable_length_quantity.js";
3
- import { writeBytesAsUintBigEndian } from "../utils/byte_functions/big_endian.js";
1
+ import { messageTypes } from "../midi_message.js";
2
+ import { writeVariableLengthQuantity } from "../../utils/byte_functions/variable_length_quantity.js";
3
+ import { writeBytesAsUintBigEndian } from "../../utils/byte_functions/big_endian.js";
4
4
 
5
5
  /**
6
6
  * Exports the midi as a standard MIDI file
@@ -1,14 +1,14 @@
1
- import { combineArrays, IndexedByteArray } from "../utils/indexed_array.js";
2
- import { writeRIFFOddSize } from "../soundfont/basic_soundfont/riff_chunk.js";
3
- import { getStringBytes, getStringBytesZero } from "../utils/byte_functions/string.js";
4
- import { messageTypes, midiControllers, MIDIMessage } from "./midi_message.js";
1
+ import { combineArrays, IndexedByteArray } from "../../utils/indexed_array.js";
2
+ import { writeRIFFOddSize } from "../../soundfont/basic_soundfont/riff_chunk.js";
3
+ import { getStringBytes, getStringBytesZero } from "../../utils/byte_functions/string.js";
4
+ import { messageTypes, midiControllers, MIDIMessage } from "../midi_message.js";
5
5
  import { getGsOn } from "./midi_editor.js";
6
- import { SpessaSynthGroup, SpessaSynthGroupEnd, SpessaSynthInfo } from "../utils/loggin.js";
7
- import { consoleColors } from "../utils/other.js";
8
- import { writeLittleEndian } from "../utils/byte_functions/little_endian.js";
9
- import { DEFAULT_PERCUSSION } from "../synthetizer/synth_constants.js";
10
- import { chooseBank, isSystemXG, parseBankSelect } from "../utils/xg_hacks.js";
11
- import { isGM2On, isGMOn, isGSDrumsOn, isGSOn, isXGOn } from "../utils/sysex_detector.js";
6
+ import { SpessaSynthGroup, SpessaSynthGroupEnd, SpessaSynthInfo } from "../../utils/loggin.js";
7
+ import { consoleColors } from "../../utils/other.js";
8
+ import { writeLittleEndian } from "../../utils/byte_functions/little_endian.js";
9
+ import { DEFAULT_PERCUSSION } from "../../synthetizer/synth_constants.js";
10
+ import { chooseBank, isSystemXG, parseBankSelect } from "../../utils/xg_hacks.js";
11
+ import { isGM2On, isGMOn, isGSDrumsOn, isGSOn, isXGOn } from "../../utils/sysex_detector.js";
12
12
 
13
13
  /**
14
14
  * @enum {string}
@@ -1,15 +1,15 @@
1
- import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd, SpessaSynthInfo } from "../utils/loggin.js";
2
- import { consoleColors } from "../utils/other.js";
3
- import { messageTypes, midiControllers } from "./midi_message.js";
4
- import { DEFAULT_PERCUSSION } from "../synthetizer/synth_constants.js";
5
- import { chooseBank, isSystemXG, parseBankSelect } from "../utils/xg_hacks.js";
6
- import { isGSDrumsOn, isXGOn } from "../utils/sysex_detector.js";
1
+ import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd, SpessaSynthInfo } from "../../utils/loggin.js";
2
+ import { consoleColors } from "../../utils/other.js";
3
+ import { messageTypes, midiControllers } from "../midi_message.js";
4
+ import { DEFAULT_PERCUSSION } from "../../synthetizer/synth_constants.js";
5
+ import { chooseBank, isSystemXG, parseBankSelect } from "../../utils/xg_hacks.js";
6
+ import { isGSDrumsOn, isXGOn } from "../../utils/sysex_detector.js";
7
7
 
8
8
  /**
9
9
  * Gets the used programs and keys for this MIDI file with a given sound bank
10
10
  * @this {BasicMIDI}
11
11
  * @param soundfont {BasicSoundBank|WorkletSoundfontManager} - the sound bank
12
- * @returns {Object<string, Set<string>>}
12
+ * @returns {Object<string, Set<string>>} Object<bank:program, Set<key-velocity>>
13
13
  */
14
14
  export function getUsedProgramsAndKeys(soundfont)
15
15
  {
@@ -3,7 +3,7 @@ import { SpessaSynthGroup, SpessaSynthGroupEnd, SpessaSynthInfo, SpessaSynthWarn
3
3
  import { consoleColors } from "../utils/other.js";
4
4
  import { readBytesAsUintBigEndian } from "../utils/byte_functions/big_endian.js";
5
5
  import { readVariableLengthQuantity } from "../utils/byte_functions/variable_length_quantity.js";
6
- import { RMIDINFOChunks } from "./rmidi_writer.js";
6
+ import { RMIDINFOChunks } from "./midi_tools/rmidi_writer.js";
7
7
  import { inflateSync } from "../externals/fflate/fflate.min.js";
8
8
  import { IndexedByteArray } from "../utils/indexed_array.js";
9
9
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spessasynth_lib",
3
- "version": "3.25.21",
3
+ "version": "3.25.22",
4
4
  "description": "MIDI and SoundFont2/DLS library with no compromises",
5
5
  "browser": "index.js",
6
6
  "type": "module",
@@ -7,7 +7,7 @@ import {
7
7
  SpessaSynthSequencerMessageType,
8
8
  SpessaSynthSequencerReturnMessageType
9
9
  } from "../worklet_wrapper/sequencer_message.js";
10
- import { messageTypes, midiControllers } from "../../midi_parser/midi_message.js";
10
+ import { messageTypes, midiControllers } from "../../midi/midi_message.js";
11
11
 
12
12
  import { MIDI_CHANNEL_COUNT } from "../../synthetizer/synth_constants.js";
13
13
 
@@ -1,9 +1,7 @@
1
- import { getEvent, messageTypes, midiControllers } from "../../midi_parser/midi_message.js";
1
+ import { getEvent, messageTypes, midiControllers } from "../../midi/midi_message.js";
2
2
  import { SpessaSynthSequencerReturnMessageType } from "../worklet_wrapper/sequencer_message.js";
3
- import { resetArray } from "../../synthetizer/audio_engine/worklet_utilities/controller_tables.js";
4
- import {
5
- nonResetableCCs
6
- } from "../../synthetizer/audio_engine/worklet_methods/controller_control/reset_controllers.js";
3
+ import { resetArray } from "../../synthetizer/audio_engine/engine_components/controller_tables.js";
4
+ import { nonResetableCCs } from "../../synthetizer/audio_engine/engine_methods/controller_control/reset_controllers.js";
7
5
 
8
6
 
9
7
  // an array with preset default values
@@ -1,4 +1,4 @@
1
- import { getEvent, messageTypes } from "../../midi_parser/midi_message.js";
1
+ import { getEvent, messageTypes } from "../../midi/midi_message.js";
2
2
  import { SpessaSynthSequencerReturnMessageType } from "../worklet_wrapper/sequencer_message.js";
3
3
  import { consoleColors } from "../../utils/other.js";
4
4
  import { SpessaSynthWarn } from "../../utils/loggin.js";
@@ -3,7 +3,7 @@ import { _addNewMidiPort, _processEvent } from "./process_event.js";
3
3
  import { _findFirstEventIndex, processTick } from "./process_tick.js";
4
4
  import { assignMIDIPort, loadNewSequence, loadNewSongList, nextSong, previousSong } from "./song_control.js";
5
5
  import { _playTo, _recalculateStartTime, play, setTimeTicks } from "./play.js";
6
- import { messageTypes, midiControllers } from "../../midi_parser/midi_message.js";
6
+ import { messageTypes, midiControllers } from "../../midi/midi_message.js";
7
7
  import {
8
8
  post,
9
9
  processMessage,
@@ -6,9 +6,9 @@ import {
6
6
  SpessaSynthInfo,
7
7
  SpessaSynthWarn
8
8
  } from "../../utils/loggin.js";
9
- import { MIDIData } from "../../midi_parser/midi_data.js";
10
- import { MIDI } from "../../midi_parser/midi_loader.js";
11
- import { BasicMIDI } from "../../midi_parser/basic_midi.js";
9
+ import { MIDIData } from "../../midi/midi_data.js";
10
+ import { MIDI } from "../../midi/midi_loader.js";
11
+ import { BasicMIDI } from "../../midi/basic_midi.js";
12
12
 
13
13
 
14
14
  /**
@@ -1,5 +1,5 @@
1
1
  import { Synthetizer } from "../../synthetizer/worklet_wrapper/synthetizer.js";
2
- import { messageTypes } from "../../midi_parser/midi_message.js";
2
+ import { messageTypes } from "../../midi/midi_message.js";
3
3
  import {
4
4
  ALL_CHANNELS_OR_DIFFERENT_ACTION,
5
5
  workletMessageType
@@ -10,8 +10,8 @@ import {
10
10
  SpessaSynthSequencerReturnMessageType
11
11
  } from "./sequencer_message.js";
12
12
  import { SpessaSynthWarn } from "../../utils/loggin.js";
13
- import { DUMMY_MIDI_DATA, MIDIData } from "../../midi_parser/midi_data.js";
14
- import { BasicMIDI } from "../../midi_parser/basic_midi.js";
13
+ import { DUMMY_MIDI_DATA, MIDIData } from "../../midi/midi_data.js";
14
+ import { BasicMIDI } from "../../midi/basic_midi.js";
15
15
  import { readBytesAsUintBigEndian } from "../../utils/byte_functions/big_endian.js";
16
16
  import { DEFAULT_SEQUENCER_OPTIONS } from "./default_sequencer_options.js";
17
17
 
@@ -1,5 +1,5 @@
1
1
  import { generatorTypes } from "./generator.js";
2
- import { midiControllers } from "../../midi_parser/midi_message.js";
2
+ import { midiControllers } from "../../midi/midi_message.js";
3
3
 
4
4
  /**
5
5
  * modulators.js
@@ -1,4 +1,4 @@
1
- import { midiControllers } from "../../../midi_parser/midi_message.js";
1
+ import { midiControllers } from "../../../midi/midi_message.js";
2
2
  import { DLSSources } from "../../dls/dls_sources.js";
3
3
  import { modulatorCurveTypes, modulatorSources } from "../modulator.js";
4
4
  import { generatorTypes } from "../generator.js";
@@ -1,6 +1,6 @@
1
1
  import { DLSSources } from "./dls_sources.js";
2
2
  import { getModSourceEnum, Modulator, modulatorCurveTypes, modulatorSources } from "../basic_soundfont/modulator.js";
3
- import { midiControllers } from "../../midi_parser/midi_message.js";
3
+ import { midiControllers } from "../../midi/midi_message.js";
4
4
  import { DLSDestinations } from "./dls_destinations.js";
5
5
 
6
6
  import { generatorTypes } from "../basic_soundfont/generator.js";
@@ -1,9 +1,9 @@
1
- ## This is the worklet system synthesis folder.
1
+ ## This is the synthesis engine folder.
2
2
 
3
3
  The code here is responsible for a single midi channel, synthesizing the sound to it.
4
4
 
5
- - `worklet_methods` contains the methods for the `main_processor.js`
6
- - `worklet_utilities` contains the various digital signal processing functions such as the wavetable oscillator, low
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
7
  pass filter, etc.
8
8
 
9
9
  For those interested, `render_voice.js` file contains the actual DSP synthesis code.
@@ -11,9 +11,9 @@ For those interested, `render_voice.js` file contains the actual DSP synthesis c
11
11
  `minify_processor.js` uses esbuild to minify the processor code. Importing this instead of `worklet_processor.js` is
12
12
  recommended.
13
13
 
14
- ## How it works
14
+ ## How it works in spessasynth_lib
15
15
  Both `Synthetizer` and `Sequencer` are essentially "remote control"
16
- for the actual sequencer and synthesizer in the audio worklet thread.
16
+ for the actual sequencer and synthesizer in the audio worklet thread (here)
17
17
  These core components are wrapped in the AudioWorkletProcessor, which is receiving both commands and data (MIDIs, sound banks)
18
18
  through the message port, and sends data back (events, time changes, status changes, etc.).
19
19
 
@@ -1,12 +1,12 @@
1
1
  import { getModulatorCurveValue, MOD_PRECOMPUTED_LENGTH } from "./modulator_curves.js";
2
- import { WorkletVolumeEnvelope } from "./volume_envelope.js";
3
- import { WorkletModulationEnvelope } from "./modulation_envelope.js";
2
+ import { VolumeEnvelope } from "./volume_envelope.js";
3
+ import { ModulationEnvelope } from "./modulation_envelope.js";
4
4
  import { generatorLimits, generatorTypes } from "../../../soundfont/basic_soundfont/generator.js";
5
5
  import { Modulator, modulatorSources } from "../../../soundfont/basic_soundfont/modulator.js";
6
6
  import { NON_CC_INDEX_OFFSET } from "./controller_tables.js";
7
7
 
8
8
  /**
9
- * worklet_modulator.js
9
+ * compute_modulator.js
10
10
  * purpose: precomputes all curve types and computes modulators
11
11
  */
12
12
 
@@ -16,10 +16,10 @@ const EFFECT_MODULATOR_TRANSFORM_MULTIPLIER = 1000 / 200;
16
16
  * Computes a given modulator
17
17
  * @param controllerTable {Int16Array} all midi controllers as 14bit values + the non-controller indexes, starting at 128
18
18
  * @param modulator {Modulator} the modulator to compute
19
- * @param voice {WorkletVoice} the voice belonging to the modulator
19
+ * @param voice {Voice} the voice belonging to the modulator
20
20
  * @returns {number} the computed value
21
21
  */
22
- export function computeWorkletModulator(controllerTable, modulator, voice)
22
+ export function computeModulator(controllerTable, modulator, voice)
23
23
  {
24
24
  if (modulator.transformAmount === 0)
25
25
  {
@@ -119,7 +119,7 @@ export function computeWorkletModulator(controllerTable, modulator, voice)
119
119
 
120
120
  /**
121
121
  * Computes modulators of a given voice. Source and index indicate what modulators shall be computed
122
- * @param voice {WorkletVoice} the voice to compute modulators for
122
+ * @param voice {Voice} the voice to compute modulators for
123
123
  * @param controllerTable {Int16Array} all midi controllers as 14bit values + the non-controller indexes, starting at 128
124
124
  * @param sourceUsesCC {number} what modulators should be computed, -1 means all, 0 means modulator source enum 1 means midi controller
125
125
  * @param sourceIndex {number} enum for the source
@@ -137,7 +137,7 @@ export function computeModulators(voice, controllerTable, sourceUsesCC = -1, sou
137
137
  modulators.forEach(mod =>
138
138
  {
139
139
  const limits = generatorLimits[mod.modulatorDestination];
140
- const newValue = modulatedGenerators[mod.modulatorDestination] + computeWorkletModulator(
140
+ const newValue = modulatedGenerators[mod.modulatorDestination] + computeModulator(
141
141
  controllerTable,
142
142
  mod,
143
143
  voice
@@ -147,8 +147,8 @@ export function computeModulators(voice, controllerTable, sourceUsesCC = -1, sou
147
147
  Math.min(newValue, limits.max)
148
148
  );
149
149
  });
150
- WorkletVolumeEnvelope.recalculate(voice);
151
- WorkletModulationEnvelope.recalculate(voice);
150
+ VolumeEnvelope.recalculate(voice);
151
+ ModulationEnvelope.recalculate(voice);
152
152
  return;
153
153
  }
154
154
 
@@ -180,7 +180,7 @@ export function computeModulators(voice, controllerTable, sourceUsesCC = -1, sou
180
180
  // Reset this destination
181
181
  modulatedGenerators[destination] = generators[destination];
182
182
  // compute our modulator
183
- computeWorkletModulator(controllerTable, mod, voice);
183
+ computeModulator(controllerTable, mod, voice);
184
184
  // sum the values of all modulators for this destination
185
185
  modulators.forEach(m =>
186
186
  {
@@ -202,10 +202,10 @@ export function computeModulators(voice, controllerTable, sourceUsesCC = -1, sou
202
202
  // Recalculate volume envelope if necessary
203
203
  if ([...computedDestinations].some(dest => volenvNeedsRecalculation.has(dest)))
204
204
  {
205
- WorkletVolumeEnvelope.recalculate(voice);
205
+ VolumeEnvelope.recalculate(voice);
206
206
  }
207
207
 
208
- WorkletModulationEnvelope.recalculate(voice);
208
+ ModulationEnvelope.recalculate(voice);
209
209
  }
210
210
 
211
211
 
@@ -1,4 +1,4 @@
1
- import { midiControllers } from "../../../midi_parser/midi_message.js";
1
+ import { midiControllers } from "../../../midi/midi_message.js";
2
2
  import { modulatorSources } from "../../../soundfont/basic_soundfont/modulator.js";
3
3
 
4
4
  /*
@@ -1,3 +1,7 @@
1
+ /**
2
+ * A manager for custom key overrides for channels
3
+ */
4
+
1
5
  export class KeyModifier
2
6
  {
3
7
 
@@ -12,18 +16,25 @@ export class KeyModifier
12
16
  */
13
17
  patch = { bank: -1, program: -1 };
14
18
 
19
+ /**
20
+ * Linear gain override for the voice
21
+ */
22
+ gain = 1;
23
+
15
24
  /**
16
25
  * @param velocity {number}
17
26
  * @param bank {number}
18
27
  * @param program {number}
28
+ * @param gain {number}
19
29
  */
20
- constructor(velocity = -1, bank = -1, program = -1)
30
+ constructor(velocity = -1, bank = -1, program = -1, gain = 1)
21
31
  {
22
32
  this.velocity = velocity;
23
33
  this.patch = {
24
34
  bank: bank,
25
35
  program: program
26
36
  };
37
+ this.gain = gain;
27
38
  }
28
39
  }
29
40
 
@@ -36,7 +47,7 @@ export const workletKeyModifierMessageType = {
36
47
  clearMappings: 2 // <no data>
37
48
  };
38
49
 
39
- export class WorkletKeyModifierManager
50
+ export class KeyModifierManager
40
51
  {
41
52
  /**
42
53
  * The velocity override mappings for MIDI keys
@@ -45,30 +56,6 @@ export class WorkletKeyModifierManager
45
56
  */
46
57
  _keyMappings = [];
47
58
 
48
- /**
49
- * @param type {workletKeyModifierMessageType}
50
- * @param data {any}
51
- */
52
- handleMessage(type, data)
53
- {
54
- switch (type)
55
- {
56
- default:
57
- return;
58
-
59
- case workletKeyModifierMessageType.addMapping:
60
- this.addMapping(...data);
61
- break;
62
-
63
- case workletKeyModifierMessageType.clearMappings:
64
- this.clearMappings();
65
- break;
66
-
67
- case workletKeyModifierMessageType.deleteMapping:
68
- this.deleteMapping(...data);
69
- }
70
- }
71
-
72
59
  /**
73
60
  * @param channel {number}
74
61
  * @param midiNote {number}
@@ -120,12 +107,17 @@ export class WorkletKeyModifierManager
120
107
  */
121
108
  getVelocity(channel, midiNote)
122
109
  {
123
- const modifier = this._keyMappings[channel]?.[midiNote];
124
- if (modifier)
125
- {
126
- return modifier.velocity;
127
- }
128
- return -1;
110
+ return this._keyMappings[channel]?.[midiNote]?.velocity ?? -1;
111
+ }
112
+
113
+ /**
114
+ * @param channel {number}
115
+ * @param midiNote {number}
116
+ * @returns {number} linear gain
117
+ */
118
+ getGain(channel, midiNote)
119
+ {
120
+ return this._keyMappings[channel]?.[midiNote]?.gain ?? 1;
129
121
  }
130
122
 
131
123
  /**
@@ -129,7 +129,7 @@ export class WorkletLowpassFilter
129
129
 
130
130
  /**
131
131
  * Applies a low-pass filter to the given buffer
132
- * @param voice {WorkletVoice} the voice we're working on
132
+ * @param voice {Voice} the voice we're working on
133
133
  * @param outputBuffer {Float32Array} the buffer to apply the filter to
134
134
  * @param fcExcursion {number} the addition of modenv and mod lfo in cents to the filter
135
135
  * @param smoothingFactor {number} filter's cutoff frequency smoothing factor