spessasynth_lib 3.24.16 → 3.24.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.
- package/midi_parser/basic_midi.js +2 -0
- package/midi_parser/midi_message.js +1 -1
- package/package.json +1 -1
- package/sequencer/sequencer.js +35 -2
- package/sequencer/worklet_sequencer/events.js +20 -7
- package/sequencer/worklet_sequencer/process_event.js +2 -6
- package/sequencer/worklet_sequencer/sequencer_message.js +8 -1
- package/sequencer/worklet_sequencer/song_control.js +5 -4
- package/sequencer/worklet_sequencer/worklet_sequencer.js +147 -86
- package/soundfont/basic_soundfont/basic_preset.js +4 -2
- package/soundfont/basic_soundfont/basic_sample.js +2 -2
- package/soundfont/basic_soundfont/modulator.js +19 -4
- package/soundfont/dls/articulator_converter.js +14 -5
- package/soundfont/dls/dls_zone.js +1 -1
- package/soundfont/dls/read_articulation.js +3 -3
- package/soundfont/dls/read_region.js +21 -9
- package/soundfont/read_sf2/samples.js +9 -5
- package/synthetizer/synth_soundfont_manager.js +2 -2
- package/synthetizer/synthetizer.js +86 -55
- package/synthetizer/worklet_processor.min.js +11 -11
- package/synthetizer/worklet_system/main_processor.js +189 -96
- package/synthetizer/worklet_system/message_protocol/handle_message.js +2 -1
- package/synthetizer/worklet_system/message_protocol/message_sending.js +5 -4
- package/synthetizer/worklet_system/snapshot/channel_snapshot.js +1 -0
- package/synthetizer/worklet_system/worklet_methods/controller_control/controller_change.js +4 -4
- package/synthetizer/worklet_system/worklet_methods/controller_control/reset_controllers.js +2 -2
- package/synthetizer/worklet_system/worklet_methods/note_on.js +9 -4
- package/synthetizer/worklet_system/worklet_methods/render_voice.js +26 -22
- package/synthetizer/worklet_system/worklet_methods/soundfont_management/clear_sound_font.js +1 -5
- package/synthetizer/worklet_system/worklet_methods/tuning_control/set_master_tuning.js +1 -1
- package/synthetizer/worklet_system/worklet_methods/tuning_control/set_modulation_depth.js +1 -1
- package/synthetizer/worklet_system/worklet_methods/tuning_control/set_octave_tuning.js +5 -1
- package/synthetizer/worklet_system/worklet_methods/tuning_control/set_tuning.js +1 -1
- package/synthetizer/worklet_system/worklet_methods/tuning_control/set_tuning_semitones.js +1 -1
- package/synthetizer/worklet_system/worklet_methods/tuning_control/transpose_channel.js +5 -2
- package/synthetizer/worklet_system/worklet_utilities/controller_tables.js +11 -2
- package/synthetizer/worklet_system/worklet_utilities/lowpass_filter.js +90 -59
- package/synthetizer/worklet_system/worklet_utilities/stereo_panner.js +13 -13
- package/synthetizer/worklet_system/worklet_utilities/worklet_modulator.js +2 -2
- package/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +35 -20
- package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +20 -16
package/package.json
CHANGED
package/sequencer/sequencer.js
CHANGED
|
@@ -3,6 +3,7 @@ import { Synthetizer } from "../synthetizer/synthetizer.js";
|
|
|
3
3
|
import { messageTypes } from "../midi_parser/midi_message.js";
|
|
4
4
|
import { workletMessageType } from "../synthetizer/worklet_system/message_protocol/worklet_message.js";
|
|
5
5
|
import {
|
|
6
|
+
SongChangeType,
|
|
6
7
|
WorkletSequencerMessageType,
|
|
7
8
|
WorkletSequencerReturnMessageType
|
|
8
9
|
} from "./worklet_sequencer/sequencer_message.js";
|
|
@@ -261,6 +262,38 @@ export class Sequencer
|
|
|
261
262
|
this._playbackRate = value;
|
|
262
263
|
}
|
|
263
264
|
|
|
265
|
+
/**
|
|
266
|
+
* @type {boolean}
|
|
267
|
+
* @private
|
|
268
|
+
*/
|
|
269
|
+
_shuffleSongs = false;
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Indicates if the song order is random
|
|
273
|
+
* @returns {boolean}
|
|
274
|
+
*/
|
|
275
|
+
get shuffleSongs()
|
|
276
|
+
{
|
|
277
|
+
return this._shuffleSongs;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Indicates if the song order is random
|
|
282
|
+
* @param value {boolean}
|
|
283
|
+
*/
|
|
284
|
+
set shuffleSongs(value)
|
|
285
|
+
{
|
|
286
|
+
this._shuffleSongs = value;
|
|
287
|
+
if (value)
|
|
288
|
+
{
|
|
289
|
+
this._sendMessage(WorkletSequencerMessageType.changeSong, SongChangeType.shuffleOn);
|
|
290
|
+
}
|
|
291
|
+
else
|
|
292
|
+
{
|
|
293
|
+
this._sendMessage(WorkletSequencerMessageType.changeSong, SongChangeType.shuffleOff);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
264
297
|
/**
|
|
265
298
|
* Indicates if the sequencer should skip to first note on
|
|
266
299
|
* @return {boolean}
|
|
@@ -446,12 +479,12 @@ export class Sequencer
|
|
|
446
479
|
|
|
447
480
|
nextSong()
|
|
448
481
|
{
|
|
449
|
-
this._sendMessage(WorkletSequencerMessageType.changeSong,
|
|
482
|
+
this._sendMessage(WorkletSequencerMessageType.changeSong, SongChangeType.forwards);
|
|
450
483
|
}
|
|
451
484
|
|
|
452
485
|
previousSong()
|
|
453
486
|
{
|
|
454
|
-
this._sendMessage(WorkletSequencerMessageType.changeSong,
|
|
487
|
+
this._sendMessage(WorkletSequencerMessageType.changeSong, SongChangeType.backwards);
|
|
455
488
|
}
|
|
456
489
|
|
|
457
490
|
/**
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
ALL_CHANNELS_OR_DIFFERENT_ACTION,
|
|
3
3
|
returnMessageType
|
|
4
4
|
} from "../../synthetizer/worklet_system/message_protocol/worklet_message.js";
|
|
5
|
-
import { WorkletSequencerMessageType, WorkletSequencerReturnMessageType } from "./sequencer_message.js";
|
|
5
|
+
import { SongChangeType, WorkletSequencerMessageType, WorkletSequencerReturnMessageType } from "./sequencer_message.js";
|
|
6
6
|
import { messageTypes, midiControllers } from "../../midi_parser/midi_message.js";
|
|
7
7
|
import { MIDI_CHANNEL_COUNT } from "../../synthetizer/synthetizer.js";
|
|
8
8
|
|
|
@@ -60,13 +60,26 @@ export function processMessage(messageType, messageData)
|
|
|
60
60
|
break;
|
|
61
61
|
|
|
62
62
|
case WorkletSequencerMessageType.changeSong:
|
|
63
|
-
|
|
63
|
+
switch (messageData)
|
|
64
64
|
{
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
case SongChangeType.forwards:
|
|
66
|
+
this.nextSong();
|
|
67
|
+
break;
|
|
68
|
+
|
|
69
|
+
case SongChangeType.backwards:
|
|
70
|
+
this.previousSong();
|
|
71
|
+
break;
|
|
72
|
+
|
|
73
|
+
case SongChangeType.shuffleOff:
|
|
74
|
+
this.shuffleMode = false;
|
|
75
|
+
this.songIndex = this.shuffledSongIndexes[this.songIndex];
|
|
76
|
+
break;
|
|
77
|
+
|
|
78
|
+
case SongChangeType.shuffleOn:
|
|
79
|
+
this.shuffleMode = true;
|
|
80
|
+
this.shuffleSongIndexes();
|
|
81
|
+
this.songIndex = 0;
|
|
82
|
+
this.loadCurrentSong();
|
|
70
83
|
}
|
|
71
84
|
break;
|
|
72
85
|
|
|
@@ -13,10 +13,6 @@ import { readBytesAsUintBigEndian } from "../../utils/byte_functions/big_endian.
|
|
|
13
13
|
*/
|
|
14
14
|
export function _processEvent(event, trackIndex)
|
|
15
15
|
{
|
|
16
|
-
if (this.ignoreEvents)
|
|
17
|
-
{
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
16
|
if (this.sendMIDIMessages)
|
|
21
17
|
{
|
|
22
18
|
if (event.messageStatusByte >= 0x80)
|
|
@@ -100,7 +96,7 @@ export function _processEvent(event, trackIndex)
|
|
|
100
96
|
}
|
|
101
97
|
break;
|
|
102
98
|
|
|
103
|
-
//
|
|
99
|
+
// recognized but ignored
|
|
104
100
|
case messageTypes.timeSignature:
|
|
105
101
|
case messageTypes.endOfTrack:
|
|
106
102
|
case messageTypes.midiChannelPrefix:
|
|
@@ -123,7 +119,7 @@ export function _processEvent(event, trackIndex)
|
|
|
123
119
|
if (statusByteData.status === messageTypes.lyric)
|
|
124
120
|
{
|
|
125
121
|
lyricsIndex = Math.min(
|
|
126
|
-
this.midiData.lyricsTicks.indexOf(event.ticks)
|
|
122
|
+
this.midiData.lyricsTicks.indexOf(event.ticks),
|
|
127
123
|
this.midiData.lyrics.length - 1
|
|
128
124
|
);
|
|
129
125
|
}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
export const SongChangeType = {
|
|
2
|
+
backwards: 0,
|
|
3
|
+
forwards: 1,
|
|
4
|
+
shuffleOn: 2,
|
|
5
|
+
shuffleOff: 3
|
|
6
|
+
};
|
|
7
|
+
|
|
1
8
|
/**
|
|
2
9
|
* @enum {number}
|
|
3
10
|
* @property {number} loadNewSongList - 0 -> [...song<MIDI>]
|
|
@@ -8,7 +15,7 @@
|
|
|
8
15
|
* @property {number} changeMIDIMessageSending - 5 -> sendMIDIMessages<boolean>
|
|
9
16
|
* @property {number} setPlaybackRate - 6 -> playbackRate<number>
|
|
10
17
|
* @property {number} setLoop - 7 -> [loop<boolean>, count<number]
|
|
11
|
-
* @property {number} changeSong - 8 ->
|
|
18
|
+
* @property {number} changeSong - 8 -> changeType<number> 0 - back, 1 - forward, 2 - shuffle ON, 3 - shuffle OFF
|
|
12
19
|
* @property {number} getMIDI - 9 -> (no data)
|
|
13
20
|
* @property {number} setSkipToFirstNote -10 -> skipToFirstNoteOn<boolean>
|
|
14
21
|
* @property {number} setPreservePlaybackState -11 -> preservePlaybackState<boolean>
|
|
@@ -120,7 +120,7 @@ export function loadNewSequence(parsedMidi, autoPlay = true)
|
|
|
120
120
|
});
|
|
121
121
|
|
|
122
122
|
/**
|
|
123
|
-
* Same as
|
|
123
|
+
* Same as "audio.duration" property (seconds)
|
|
124
124
|
* @type {number}
|
|
125
125
|
*/
|
|
126
126
|
this.duration = this.midiData.duration;
|
|
@@ -188,7 +188,8 @@ export function loadNewSongList(midiBuffers, autoPlay = true)
|
|
|
188
188
|
{
|
|
189
189
|
this.loop = false;
|
|
190
190
|
}
|
|
191
|
-
this.
|
|
191
|
+
this.shuffleSongIndexes();
|
|
192
|
+
this.loadCurrentSong(autoPlay);
|
|
192
193
|
}
|
|
193
194
|
|
|
194
195
|
/**
|
|
@@ -203,7 +204,7 @@ export function nextSong()
|
|
|
203
204
|
}
|
|
204
205
|
this.songIndex++;
|
|
205
206
|
this.songIndex %= this.songs.length;
|
|
206
|
-
this.
|
|
207
|
+
this.loadCurrentSong();
|
|
207
208
|
}
|
|
208
209
|
|
|
209
210
|
/**
|
|
@@ -221,5 +222,5 @@ export function previousSong()
|
|
|
221
222
|
{
|
|
222
223
|
this.songIndex = this.songs.length - 1;
|
|
223
224
|
}
|
|
224
|
-
this.
|
|
225
|
+
this.loadCurrentSong();
|
|
225
226
|
}
|
|
@@ -18,100 +18,139 @@ import { MIDI_CHANNEL_COUNT } from "../../synthetizer/synthetizer.js";
|
|
|
18
18
|
|
|
19
19
|
class WorkletSequencer
|
|
20
20
|
{
|
|
21
|
+
/**
|
|
22
|
+
* All the sequencer's songs
|
|
23
|
+
* @type {BasicMIDI[]}
|
|
24
|
+
*/
|
|
25
|
+
songs = [];
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Current song index
|
|
29
|
+
* @type {number}
|
|
30
|
+
*/
|
|
31
|
+
songIndex = 0;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* shuffled song indexes
|
|
35
|
+
* @type {number[]}
|
|
36
|
+
*/
|
|
37
|
+
shuffledSongIndexes = [];
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* the synth to use
|
|
41
|
+
* @type {SpessaSynthProcessor}
|
|
42
|
+
*/
|
|
43
|
+
synth;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* if the sequencer is active
|
|
47
|
+
* @type {boolean}
|
|
48
|
+
*/
|
|
49
|
+
isActive = false;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* If the event should instead be sent back to the main thread instead of synth
|
|
53
|
+
* @type {boolean}
|
|
54
|
+
*/
|
|
55
|
+
sendMIDIMessages = false;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* sequencer's loop count
|
|
59
|
+
* @type {number}
|
|
60
|
+
*/
|
|
61
|
+
loopCount = Infinity;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* event's number in this.events
|
|
65
|
+
* @type {number[]}
|
|
66
|
+
*/
|
|
67
|
+
eventIndex = [];
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* tracks the time that has already been played
|
|
71
|
+
* @type {number}
|
|
72
|
+
*/
|
|
73
|
+
playedTime = 0;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The (relative) time when the sequencer was paused. If it's not paused, then it's undefined.
|
|
77
|
+
* @type {number}
|
|
78
|
+
*/
|
|
79
|
+
pausedTime = undefined;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Absolute playback startTime, bases on the synth's time
|
|
83
|
+
* @type {number}
|
|
84
|
+
*/
|
|
85
|
+
absoluteStartTime = currentTime;
|
|
86
|
+
/**
|
|
87
|
+
* Currently playing notes (for pausing and resuming)
|
|
88
|
+
* @type {{
|
|
89
|
+
* midiNote: number,
|
|
90
|
+
* channel: number,
|
|
91
|
+
* velocity: number
|
|
92
|
+
* }[]}
|
|
93
|
+
*/
|
|
94
|
+
playingNotes = [];
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* controls if the sequencer loops (defaults to true)
|
|
98
|
+
* @type {boolean}
|
|
99
|
+
*/
|
|
100
|
+
loop = true;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* controls if the songs are ordered randomly
|
|
104
|
+
* @type {boolean}
|
|
105
|
+
*/
|
|
106
|
+
shuffleMode = false;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* the current track data
|
|
110
|
+
* @type {BasicMIDI}
|
|
111
|
+
*/
|
|
112
|
+
midiData = undefined;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* midi port number for the corresponding track
|
|
116
|
+
* @type {number[]}
|
|
117
|
+
*/
|
|
118
|
+
midiPorts = [];
|
|
119
|
+
midiPortChannelOffset = 0;
|
|
120
|
+
/**
|
|
121
|
+
* stored as:
|
|
122
|
+
* Object<midi port, channel offset>
|
|
123
|
+
* @type {Object<number, number>}
|
|
124
|
+
*/
|
|
125
|
+
midiPortChannelOffsets = {};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @type {boolean}
|
|
129
|
+
* @private
|
|
130
|
+
*/
|
|
131
|
+
_skipToFirstNoteOn = true;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* If true, seq will stay paused when seeking or changing the playback rate
|
|
135
|
+
* @type {boolean}
|
|
136
|
+
*/
|
|
137
|
+
preservePlaybackState = false;
|
|
138
|
+
|
|
21
139
|
/**
|
|
22
140
|
* @param spessasynthProcessor {SpessaSynthProcessor}
|
|
23
141
|
*/
|
|
24
142
|
constructor(spessasynthProcessor)
|
|
25
143
|
{
|
|
26
144
|
this.synth = spessasynthProcessor;
|
|
27
|
-
this.ignoreEvents = false;
|
|
28
|
-
this.isActive = false;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* If the event should instead be sent back to the main thread instead of synth
|
|
32
|
-
* @type {boolean}
|
|
33
|
-
*/
|
|
34
|
-
this.sendMIDIMessages = false;
|
|
35
|
-
|
|
36
|
-
this.loopCount = Infinity;
|
|
37
|
-
|
|
38
|
-
// event's number in this.events
|
|
39
|
-
/**
|
|
40
|
-
* @type {number[]}
|
|
41
|
-
*/
|
|
42
|
-
this.eventIndex = [];
|
|
43
|
-
this.songIndex = 0;
|
|
44
|
-
|
|
45
|
-
// tracks the time that we have already played
|
|
46
|
-
/**
|
|
47
|
-
* @type {number}
|
|
48
|
-
*/
|
|
49
|
-
this.playedTime = 0;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* The (relative) time when the sequencer was paused. If it's not paused then it's undefined.
|
|
53
|
-
* @type {number}
|
|
54
|
-
*/
|
|
55
|
-
this.pausedTime = undefined;
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Absolute playback startTime, bases on the synth's time
|
|
59
|
-
* @type {number}
|
|
60
|
-
*/
|
|
61
|
-
this.absoluteStartTime = currentTime;
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Controls the playback's rate
|
|
65
|
-
* @type {number}
|
|
66
|
-
*/
|
|
67
|
-
this._playbackRate = 1;
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Currently playing notes (for pausing and resuming)
|
|
71
|
-
* @type {{
|
|
72
|
-
* midiNote: number,
|
|
73
|
-
* channel: number,
|
|
74
|
-
* velocity: number
|
|
75
|
-
* }[]}
|
|
76
|
-
*/
|
|
77
|
-
this.playingNotes = [];
|
|
78
|
-
|
|
79
|
-
// controls if the sequencer loops (defaults to true)
|
|
80
|
-
this.loop = true;
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* the current track data
|
|
84
|
-
* @type {BasicMIDI}
|
|
85
|
-
*/
|
|
86
|
-
this.midiData = undefined;
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* midi port number for the corresponding track
|
|
90
|
-
* @type {number[]}
|
|
91
|
-
*/
|
|
92
|
-
this.midiPorts = [];
|
|
93
|
-
|
|
94
|
-
this.midiPortChannelOffset = 0;
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* midi port: channel offset
|
|
98
|
-
* @type {Object<number, number>}
|
|
99
|
-
*/
|
|
100
|
-
this.midiPortChannelOffsets = {};
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* @type {boolean}
|
|
104
|
-
* @private
|
|
105
|
-
*/
|
|
106
|
-
this._skipToFirstNoteOn = true;
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* If true, seq will stay paused when seeking or changing the playback rate
|
|
110
|
-
* @type {boolean}
|
|
111
|
-
*/
|
|
112
|
-
this.preservePlaybackState = false;
|
|
113
145
|
}
|
|
114
146
|
|
|
147
|
+
/**
|
|
148
|
+
* Controls the playback's rate
|
|
149
|
+
* @type {number}
|
|
150
|
+
* @private
|
|
151
|
+
*/
|
|
152
|
+
_playbackRate = 1;
|
|
153
|
+
|
|
115
154
|
/**
|
|
116
155
|
* @param value {number}
|
|
117
156
|
*/
|
|
@@ -229,6 +268,16 @@ class WorkletSequencer
|
|
|
229
268
|
}
|
|
230
269
|
}
|
|
231
270
|
|
|
271
|
+
loadCurrentSong(autoPlay = true)
|
|
272
|
+
{
|
|
273
|
+
let index = this.songIndex;
|
|
274
|
+
if (this.shuffleMode)
|
|
275
|
+
{
|
|
276
|
+
index = this.shuffledSongIndexes[this.songIndex];
|
|
277
|
+
}
|
|
278
|
+
this.loadNewSequence(this.songs[index], autoPlay);
|
|
279
|
+
}
|
|
280
|
+
|
|
232
281
|
_resetTimers()
|
|
233
282
|
{
|
|
234
283
|
this.playedTime = 0;
|
|
@@ -244,6 +293,18 @@ class WorkletSequencer
|
|
|
244
293
|
{
|
|
245
294
|
this.isActive = false;
|
|
246
295
|
}
|
|
296
|
+
|
|
297
|
+
shuffleSongIndexes()
|
|
298
|
+
{
|
|
299
|
+
const indexes = this.songs.map((_, i) => i);
|
|
300
|
+
this.shuffledSongIndexes = [];
|
|
301
|
+
while (indexes.length > 0)
|
|
302
|
+
{
|
|
303
|
+
const index = indexes[Math.floor(Math.random() * indexes.length)];
|
|
304
|
+
this.shuffledSongIndexes.push(index);
|
|
305
|
+
indexes.splice(indexes.indexOf(index), 1);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
247
308
|
}
|
|
248
309
|
|
|
249
310
|
// Web MIDI sending
|
|
@@ -93,6 +93,7 @@ export class BasicPreset
|
|
|
93
93
|
this.presetZones.splice(index, 1);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
// noinspection JSUnusedGlobalSymbols
|
|
96
97
|
/**
|
|
97
98
|
* Preloads all samples (async)
|
|
98
99
|
*/
|
|
@@ -210,7 +211,7 @@ export class BasicPreset
|
|
|
210
211
|
|
|
211
212
|
presetZonesInRange.forEach(zone =>
|
|
212
213
|
{
|
|
213
|
-
// global zone is already taken into account earlier
|
|
214
|
+
// the global zone is already taken into account earlier
|
|
214
215
|
if (zone.instrument.instrumentZones.length < 1)
|
|
215
216
|
{
|
|
216
217
|
return;
|
|
@@ -288,7 +289,8 @@ export class BasicPreset
|
|
|
288
289
|
m => Modulator.isIdentical(mod, m));
|
|
289
290
|
if (identicalInstrumentModulator !== -1)
|
|
290
291
|
{
|
|
291
|
-
// sum the amounts
|
|
292
|
+
// sum the amounts
|
|
293
|
+
// (this makes a new modulator because otherwise it would overwrite the one in the soundfont!
|
|
292
294
|
finalModulatorList[identicalInstrumentModulator] = finalModulatorList[identicalInstrumentModulator].sumTransform(
|
|
293
295
|
mod);
|
|
294
296
|
}
|
|
@@ -63,12 +63,12 @@ export class BasicSample
|
|
|
63
63
|
*/
|
|
64
64
|
this.sampleType = sampleType;
|
|
65
65
|
/**
|
|
66
|
-
* Relative to start of the sample in sample points
|
|
66
|
+
* Relative to the start of the sample in sample points
|
|
67
67
|
* @type {number}
|
|
68
68
|
*/
|
|
69
69
|
this.sampleLoopStartIndex = loopStart;
|
|
70
70
|
/**
|
|
71
|
-
* Relative to start of the sample in sample points
|
|
71
|
+
* Relative to the start of the sample in sample points
|
|
72
72
|
* @type {number}
|
|
73
73
|
*/
|
|
74
74
|
this.sampleLoopEndIndex = loopEnd;
|
|
@@ -121,7 +121,7 @@ export class Modulator
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
/**
|
|
124
|
-
*
|
|
124
|
+
* Sum transform and create a NEW modulator
|
|
125
125
|
* @param modulator {Modulator}
|
|
126
126
|
* @returns {Modulator}
|
|
127
127
|
*/
|
|
@@ -279,6 +279,21 @@ const customModulators = [
|
|
|
279
279
|
transform: 0
|
|
280
280
|
}),
|
|
281
281
|
|
|
282
|
+
// cc 73 (attack time) to volEnv attack
|
|
283
|
+
new Modulator({
|
|
284
|
+
srcEnum: getModSourceEnum(
|
|
285
|
+
modulatorCurveTypes.convex,
|
|
286
|
+
1,
|
|
287
|
+
0,
|
|
288
|
+
1,
|
|
289
|
+
midiControllers.attackTime
|
|
290
|
+
), // linear forward bipolar cc 72
|
|
291
|
+
dest: generatorTypes.attackVolEnv,
|
|
292
|
+
amt: 6000,
|
|
293
|
+
secSrcEnum: 0x0, // no controller
|
|
294
|
+
transform: 0
|
|
295
|
+
}),
|
|
296
|
+
|
|
282
297
|
// cc 72 (release time) to volEnv release
|
|
283
298
|
new Modulator({
|
|
284
299
|
srcEnum: getModSourceEnum(
|
|
@@ -289,7 +304,7 @@ const customModulators = [
|
|
|
289
304
|
midiControllers.releaseTime
|
|
290
305
|
), // linear forward bipolar cc 72
|
|
291
306
|
dest: generatorTypes.releaseVolEnv,
|
|
292
|
-
amt:
|
|
307
|
+
amt: 3600,
|
|
293
308
|
secSrcEnum: 0x0, // no controller
|
|
294
309
|
transform: 0
|
|
295
310
|
}),
|
|
@@ -309,14 +324,14 @@ const customModulators = [
|
|
|
309
324
|
transform: 0
|
|
310
325
|
}),
|
|
311
326
|
|
|
312
|
-
// cc 71 (filter
|
|
327
|
+
// cc 71 (filter Q) to filter Q
|
|
313
328
|
new Modulator({
|
|
314
329
|
srcEnum: getModSourceEnum(
|
|
315
330
|
modulatorCurveTypes.linear,
|
|
316
331
|
1,
|
|
317
332
|
0,
|
|
318
333
|
1,
|
|
319
|
-
midiControllers.
|
|
334
|
+
midiControllers.filterResonance
|
|
320
335
|
), // linear forwards bipolar cc 74
|
|
321
336
|
dest: generatorTypes.initialFilterQ,
|
|
322
337
|
amt: 250,
|
|
@@ -136,7 +136,7 @@ function getSF2GeneratorFromDLS(destination, amount)
|
|
|
136
136
|
case DLSDestinations.reverbSend:
|
|
137
137
|
return generatorTypes.reverbEffectsSend;
|
|
138
138
|
|
|
139
|
-
//
|
|
139
|
+
// lfo
|
|
140
140
|
case DLSDestinations.modLfoFreq:
|
|
141
141
|
return generatorTypes.freqModLFO;
|
|
142
142
|
case DLSDestinations.modLfoDelay:
|
|
@@ -319,8 +319,9 @@ export function getSF2ModulatorFromArticulator(
|
|
|
319
319
|
if (isSourceNoController)
|
|
320
320
|
{
|
|
321
321
|
// we force it into this state because before it was some strange value,
|
|
322
|
-
// like vibrato lfo bipolar for example
|
|
323
|
-
// since we turn it into NoController -> vibLfoToPitch
|
|
322
|
+
// like vibrato lfo bipolar, for example,
|
|
323
|
+
// since we turn it into NoController -> vibLfoToPitch,
|
|
324
|
+
// the result is the same and bipolar controller is technically 0
|
|
324
325
|
sourceEnumFinal = 0x0;
|
|
325
326
|
}
|
|
326
327
|
else
|
|
@@ -328,7 +329,7 @@ export function getSF2ModulatorFromArticulator(
|
|
|
328
329
|
// output transform is ignored as it's not a thing in sfont format
|
|
329
330
|
// unless the curve type of source is linear, then output is copied
|
|
330
331
|
const outputTransform = transform & 0b1111;
|
|
331
|
-
// source curve type maps to desfont curve type in section 2.10, table 9
|
|
332
|
+
// source curve type maps to a desfont curve type in section 2.10, table 9
|
|
332
333
|
let sourceTransform = (transform >> 10) & 0b1111;
|
|
333
334
|
if (sourceTransform === modulatorCurveTypes.linear && outputTransform !== modulatorCurveTypes.linear)
|
|
334
335
|
{
|
|
@@ -341,7 +342,7 @@ export function getSF2ModulatorFromArticulator(
|
|
|
341
342
|
{
|
|
342
343
|
// if the value is negative, the source shall be negative!
|
|
343
344
|
// why?
|
|
344
|
-
//
|
|
345
|
+
// IDK, it makes it work with ROCK.RMI and NOKIA_S30.dls
|
|
345
346
|
if (value < 0)
|
|
346
347
|
{
|
|
347
348
|
sourceIsNegative = 1;
|
|
@@ -356,6 +357,14 @@ export function getSF2ModulatorFromArticulator(
|
|
|
356
357
|
);
|
|
357
358
|
}
|
|
358
359
|
|
|
360
|
+
// a corrupted rendition of gm.dls was found under
|
|
361
|
+
// https://sembiance.com/fileFormatSamples/audio/downloadableSoundBank/
|
|
362
|
+
// which specifies a whopping -32,768 decibels of attenuation
|
|
363
|
+
if (destinationGenerator === generatorTypes.initialAttenuation)
|
|
364
|
+
{
|
|
365
|
+
newValue = Math.max(960, Math.min(0, newValue));
|
|
366
|
+
}
|
|
367
|
+
|
|
359
368
|
const secSourceTransform = (transform >> 4) & 0b1111;
|
|
360
369
|
const secSourceIsBipolar = (transform >> 8) & 1;
|
|
361
370
|
const secSourceIsNegative = transform >> 9 & 1;
|
|
@@ -76,7 +76,7 @@ export function readArticulation(chunk, disableVibrato)
|
|
|
76
76
|
generator = new Generator(generatorTypes.delayVibLFO, value);
|
|
77
77
|
break;
|
|
78
78
|
|
|
79
|
-
// vol env: all times are timecents like sf2
|
|
79
|
+
// vol. env: all times are timecents like sf2
|
|
80
80
|
case DLSDestinations.volEnvDelay:
|
|
81
81
|
generator = new Generator(generatorTypes.delayVolEnv, value);
|
|
82
82
|
break;
|
|
@@ -144,7 +144,7 @@ export function readArticulation(chunk, disableVibrato)
|
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
else
|
|
147
|
-
// if not, modulator
|
|
147
|
+
// if not, modulator?
|
|
148
148
|
{
|
|
149
149
|
let isGenerator = true;
|
|
150
150
|
// a few special cases which are generators:
|
|
@@ -190,7 +190,7 @@ export function readArticulation(chunk, disableVibrato)
|
|
|
190
190
|
if (source === DLSSources.keyNum && destination === DLSDestinations.pitch)
|
|
191
191
|
{
|
|
192
192
|
// this is just a soundfont generator, but the amount must be changed
|
|
193
|
-
//
|
|
193
|
+
// 12,800 means the regular scale (100)
|
|
194
194
|
generators.push(new Generator(generatorTypes.scaleTuning, value / 128));
|
|
195
195
|
}
|
|
196
196
|
else
|