spessasynth_lib 3.21.0 → 3.21.2
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/@types/index.d.ts +2 -0
- package/index.js +2 -2
- package/package.json +1 -1
- package/soundfont/basic_soundfont/modulator.js +0 -0
- package/synthetizer/worklet_processor.min.js +9 -9
- package/synthetizer/worklet_system/worklet_methods/note_off.js +4 -9
- package/synthetizer/worklet_system/worklet_methods/note_on.js +3 -1
- package/synthetizer/worklet_system/worklet_methods/voice_control.js +1 -1
- package/synthetizer/worklet_system/worklet_utilities/controller_tables.js +0 -0
- package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +46 -33
|
@@ -16,12 +16,7 @@ export function noteOff(channel, midiNote)
|
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
let
|
|
20
|
-
const program = this.workletProcessorChannels[channel].preset.program;
|
|
21
|
-
if (this.tunings[program]?.[midiNote]?.midiNote >= 0)
|
|
22
|
-
{
|
|
23
|
-
actualNote = this.tunings[program]?.[midiNote].midiNote;
|
|
24
|
-
}
|
|
19
|
+
let realKey = midiNote + this.workletProcessorChannels[channel].channelTransposeKeyShift;
|
|
25
20
|
|
|
26
21
|
// if high performance mode, kill notes instead of stopping them
|
|
27
22
|
if (this.highPerformanceMode)
|
|
@@ -29,7 +24,7 @@ export function noteOff(channel, midiNote)
|
|
|
29
24
|
// if the channel is percussion channel, do not kill the notes
|
|
30
25
|
if (!this.workletProcessorChannels[channel].drumChannel)
|
|
31
26
|
{
|
|
32
|
-
this.killNote(channel,
|
|
27
|
+
this.killNote(channel, realKey);
|
|
33
28
|
return;
|
|
34
29
|
}
|
|
35
30
|
}
|
|
@@ -37,7 +32,7 @@ export function noteOff(channel, midiNote)
|
|
|
37
32
|
const channelVoices = this.workletProcessorChannels[channel].voices;
|
|
38
33
|
channelVoices.forEach(v =>
|
|
39
34
|
{
|
|
40
|
-
if (v.
|
|
35
|
+
if (v.realKey !== realKey || v.isInRelease === true)
|
|
41
36
|
{
|
|
42
37
|
return;
|
|
43
38
|
}
|
|
@@ -67,7 +62,7 @@ export function killNote(channel, midiNote)
|
|
|
67
62
|
{
|
|
68
63
|
this.workletProcessorChannels[channel].voices.forEach(v =>
|
|
69
64
|
{
|
|
70
|
-
if (v.
|
|
65
|
+
if (v.realKey !== midiNote)
|
|
71
66
|
{
|
|
72
67
|
return;
|
|
73
68
|
}
|
|
@@ -31,7 +31,8 @@ export function noteOn(channel, midiNote, velocity, enableDebugging = false, sen
|
|
|
31
31
|
return;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
const realKey = midiNote + channelObject.channelTransposeKeyShift;
|
|
35
|
+
let sentMidiNote = realKey;
|
|
35
36
|
|
|
36
37
|
if (midiNote > 127 || midiNote < 0)
|
|
37
38
|
{
|
|
@@ -56,6 +57,7 @@ export function noteOn(channel, midiNote, velocity, enableDebugging = false, sen
|
|
|
56
57
|
velocity,
|
|
57
58
|
channelObject,
|
|
58
59
|
startTime,
|
|
60
|
+
realKey,
|
|
59
61
|
enableDebugging
|
|
60
62
|
);
|
|
61
63
|
|
|
@@ -78,7 +78,7 @@ export function renderVoice(
|
|
|
78
78
|
+ channel.customControllers[customControllers.channelTuningSemitones]; // RPN channel coarse tuning
|
|
79
79
|
|
|
80
80
|
// midi tuning standard
|
|
81
|
-
const tuning = this.tunings[channel.preset.program]?.[
|
|
81
|
+
const tuning = this.tunings[channel.preset.program]?.[voice.realKey];
|
|
82
82
|
if (tuning?.midiNote >= 0)
|
|
83
83
|
{
|
|
84
84
|
// override key
|
|
File without changes
|
|
@@ -111,123 +111,131 @@ import { Modulator } from "../../../soundfont/basic_soundfont/modulator.js";
|
|
|
111
111
|
class WorkletVoice
|
|
112
112
|
{
|
|
113
113
|
/**
|
|
114
|
-
*
|
|
114
|
+
* The sample of the voice.
|
|
115
115
|
* @type {WorkletSample}
|
|
116
116
|
*/
|
|
117
117
|
sample;
|
|
118
118
|
|
|
119
119
|
/**
|
|
120
|
-
* Lowpass filter
|
|
121
|
-
the voice.
|
|
120
|
+
* Lowpass filter applied to the voice.
|
|
122
121
|
* @type {WorkletLowpassFilter}
|
|
123
122
|
*/
|
|
124
123
|
filter = new WorkletLowpassFilter();
|
|
125
124
|
|
|
126
125
|
/**
|
|
127
|
-
* The unmodulated (
|
|
126
|
+
* The unmodulated (copied to) generators of the voice.
|
|
128
127
|
* @type {Int16Array}
|
|
129
128
|
*/
|
|
130
129
|
generators;
|
|
131
130
|
|
|
132
131
|
/**
|
|
133
|
-
* The voice's
|
|
132
|
+
* The voice's modulators.
|
|
134
133
|
* @type {Modulator[]}
|
|
135
134
|
*/
|
|
136
135
|
modulators = [];
|
|
137
136
|
|
|
138
137
|
/**
|
|
139
|
-
* The generators
|
|
140
|
-
|
|
138
|
+
* The generators in real-time, affected by modulators.
|
|
139
|
+
* This is used during rendering.
|
|
141
140
|
* @type {Int16Array}
|
|
142
141
|
*/
|
|
143
142
|
modulatedGenerators;
|
|
144
143
|
|
|
145
144
|
/**
|
|
146
|
-
* Indicates if the
|
|
145
|
+
* Indicates if the voice is finished.
|
|
147
146
|
* @type {boolean}
|
|
148
147
|
*/
|
|
149
148
|
finished = false;
|
|
150
149
|
|
|
151
150
|
/**
|
|
152
|
-
* Indicates if the voice
|
|
151
|
+
* Indicates if the voice is in the release phase.
|
|
153
152
|
* @type {boolean}
|
|
154
153
|
*/
|
|
155
154
|
isInRelease = false;
|
|
156
155
|
|
|
157
156
|
/**
|
|
158
|
-
* MIDI channel
|
|
157
|
+
* MIDI channel number.
|
|
158
|
+
* @type {number}
|
|
159
159
|
*/
|
|
160
160
|
channelNumber = 0;
|
|
161
161
|
|
|
162
162
|
/**
|
|
163
|
-
* Velocity of the
|
|
163
|
+
* Velocity of the note.
|
|
164
164
|
* @type {number}
|
|
165
165
|
*/
|
|
166
166
|
velocity = 0;
|
|
167
167
|
|
|
168
168
|
/**
|
|
169
169
|
* MIDI note number.
|
|
170
|
-
|
|
171
|
-
@type {number}
|
|
170
|
+
* @type {number}
|
|
172
171
|
*/
|
|
173
172
|
midiNote = 0;
|
|
174
173
|
|
|
175
174
|
/**
|
|
176
|
-
* The pressure of the
|
|
175
|
+
* The pressure of the voice
|
|
177
176
|
* @type {number}
|
|
178
177
|
*/
|
|
179
178
|
pressure = 0;
|
|
180
179
|
|
|
181
180
|
/**
|
|
182
|
-
* Target key for the
|
|
181
|
+
* Target key for the note.
|
|
182
|
+
* @type {number}
|
|
183
183
|
*/
|
|
184
184
|
targetKey = 0;
|
|
185
185
|
|
|
186
186
|
/**
|
|
187
|
-
* Modulation
|
|
187
|
+
* Modulation envelope.
|
|
188
188
|
* @type {WorkletModulationEnvelope}
|
|
189
189
|
*/
|
|
190
190
|
modulationEnvelope = new WorkletModulationEnvelope();
|
|
191
191
|
|
|
192
192
|
/**
|
|
193
193
|
* Volume envelope.
|
|
194
|
-
|
|
195
|
-
type {WorkletVolumeEnvelope}
|
|
194
|
+
* @type {WorkletVolumeEnvelope}
|
|
196
195
|
*/
|
|
197
196
|
volumeEnvelope;
|
|
198
197
|
|
|
199
198
|
/**
|
|
200
|
-
* Start time of the
|
|
199
|
+
* Start time of the voice, absolute.
|
|
201
200
|
* @type {number}
|
|
202
201
|
*/
|
|
203
202
|
startTime = 0;
|
|
204
203
|
|
|
205
204
|
/**
|
|
206
|
-
* Start time of the
|
|
205
|
+
* Start time of the release phase, absolute.
|
|
207
206
|
* @type {number}
|
|
208
207
|
*/
|
|
209
208
|
releaseStartTime = Infinity;
|
|
210
209
|
|
|
211
210
|
/**
|
|
212
|
-
* Current tuning
|
|
211
|
+
* Current tuning in cents.
|
|
213
212
|
* @type {number}
|
|
214
213
|
*/
|
|
215
214
|
currentTuningCents = 0;
|
|
216
215
|
|
|
217
216
|
/**
|
|
218
|
-
*
|
|
217
|
+
* Current calculated tuning. (as in ratio)
|
|
219
218
|
* @type {number}
|
|
220
219
|
*/
|
|
221
220
|
currentTuningCalculated = 1;
|
|
222
221
|
|
|
223
222
|
/**
|
|
224
223
|
* From 0 to 1.
|
|
225
|
-
*
|
|
224
|
+
* @param {number}
|
|
226
225
|
*/
|
|
227
226
|
currentPan = 0.5;
|
|
228
227
|
|
|
229
228
|
/**
|
|
230
|
-
*
|
|
229
|
+
* If MIDI Tuning Standard is already applied (at note-on time),
|
|
230
|
+
* this will be used to take the values at real-time tuning as "midiNote"
|
|
231
|
+
* property contains the tuned number.
|
|
232
|
+
* see #29 comment by @paulikaro
|
|
233
|
+
* @type {number}
|
|
234
|
+
*/
|
|
235
|
+
realKey;
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Creates a workletVoice
|
|
231
239
|
* @param sampleRate {number}
|
|
232
240
|
* @param workletSample {WorkletSample}
|
|
233
241
|
* @param midiNote {number}
|
|
@@ -235,6 +243,7 @@ class WorkletVoice
|
|
|
235
243
|
* @param channel {number}
|
|
236
244
|
* @param currentTime {number}
|
|
237
245
|
* @param targetKey {number}
|
|
246
|
+
* @param realKey {number}
|
|
238
247
|
* @param generators {Int16Array}
|
|
239
248
|
* @param modulators {Modulator[]}
|
|
240
249
|
*/
|
|
@@ -246,6 +255,7 @@ class WorkletVoice
|
|
|
246
255
|
channel,
|
|
247
256
|
currentTime,
|
|
248
257
|
targetKey,
|
|
258
|
+
realKey,
|
|
249
259
|
generators,
|
|
250
260
|
modulators
|
|
251
261
|
)
|
|
@@ -260,12 +270,12 @@ class WorkletVoice
|
|
|
260
270
|
this.channelNumber = channel;
|
|
261
271
|
this.startTime = currentTime;
|
|
262
272
|
this.targetKey = targetKey;
|
|
273
|
+
this.realKey = realKey;
|
|
263
274
|
this.volumeEnvelope = new WorkletVolumeEnvelope(sampleRate, generators[generatorTypes.sustainVolEnv]);
|
|
264
275
|
}
|
|
265
276
|
|
|
266
277
|
/**
|
|
267
|
-
*
|
|
268
|
-
tance
|
|
278
|
+
* copies the voice
|
|
269
279
|
* @param voice {WorkletVoice}
|
|
270
280
|
* @param currentTime {number}
|
|
271
281
|
* @returns WorkletVoice
|
|
@@ -291,6 +301,7 @@ class WorkletVoice
|
|
|
291
301
|
voice.channelNumber,
|
|
292
302
|
currentTime,
|
|
293
303
|
voice.targetKey,
|
|
304
|
+
voice.realKey,
|
|
294
305
|
voice.generators,
|
|
295
306
|
voice.modulators.map(m => Modulator.copy(m))
|
|
296
307
|
);
|
|
@@ -299,20 +310,21 @@ class WorkletVoice
|
|
|
299
310
|
|
|
300
311
|
/**
|
|
301
312
|
* @param channel {number} a hint for the processor to recalculate sample cursors when sample dumping
|
|
302
|
-
* @param midiNote {number}
|
|
303
|
-
* @param velocity {number}
|
|
304
|
-
* @param channelObject {WorkletProcessorChannel}
|
|
305
|
-
* @param currentTime {number}
|
|
306
|
-
*
|
|
307
|
-
* @param debug {boolean}
|
|
313
|
+
* @param midiNote {number} the MIDI note to use
|
|
314
|
+
* @param velocity {number} the velocity to use
|
|
315
|
+
* @param channelObject {WorkletProcessorChannel} the channel this will belong to
|
|
316
|
+
* @param currentTime {number} the current time in seconds
|
|
317
|
+
* @param realKey {number} the real MIDI note if the "midiNote" was changed by MIDI Tuning Standard
|
|
318
|
+
* @param debug {boolean} enable debugging?
|
|
308
319
|
* @this {SpessaSynthProcessor}
|
|
309
|
-
* @returns {WorkletVoice[]}
|
|
320
|
+
* @returns {WorkletVoice[]} output is an array of WorkletVoices
|
|
310
321
|
*/
|
|
311
322
|
export function getWorkletVoices(channel,
|
|
312
323
|
midiNote,
|
|
313
324
|
velocity,
|
|
314
325
|
channelObject,
|
|
315
326
|
currentTime,
|
|
327
|
+
realKey,
|
|
316
328
|
debug = false)
|
|
317
329
|
{
|
|
318
330
|
/**
|
|
@@ -419,6 +431,7 @@ export function getWorkletVoices(channel,
|
|
|
419
431
|
channel,
|
|
420
432
|
currentTime,
|
|
421
433
|
targetKey,
|
|
434
|
+
realKey,
|
|
422
435
|
generators,
|
|
423
436
|
sampleAndGenerators.modulators.map(m => Modulator.copy(m))
|
|
424
437
|
)
|