spessasynth_core 3.27.2 → 3.27.3
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/package.json +1 -1
- package/src/soundfont/basic_soundfont/basic_sample.js +1 -1
- package/src/synthetizer/audio_engine/engine_components/soundfont_manager.js +0 -3
- package/src/synthetizer/audio_engine/engine_components/wavetable_oscillator.js +80 -59
- package/src/synthetizer/audio_engine/engine_methods/render_voice.js +13 -15
package/package.json
CHANGED
|
@@ -116,9 +116,6 @@ export class SoundFontManager
|
|
|
116
116
|
SpessaSynthInfo(`No soundfont with id of "${id}" found. Aborting!`);
|
|
117
117
|
return;
|
|
118
118
|
}
|
|
119
|
-
delete this.soundfontList[index].soundfont.presets;
|
|
120
|
-
delete this.soundfontList[index].soundfont.instruments;
|
|
121
|
-
delete this.soundfontList[index].soundfont.samples;
|
|
122
119
|
this.soundfontList.splice(index, 1);
|
|
123
120
|
this.generatePresetList();
|
|
124
121
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { interpolationTypes } from "./enums.js";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* wavetable_oscillator.js
|
|
3
5
|
* purpose: plays back raw audio data at an arbitrary playback rate
|
|
@@ -6,13 +8,45 @@
|
|
|
6
8
|
|
|
7
9
|
export class WavetableOscillator
|
|
8
10
|
{
|
|
11
|
+
/**
|
|
12
|
+
* Fills the output buffer with raw sample data using a given interpolation
|
|
13
|
+
* @param voice {Voice} the voice we're working on
|
|
14
|
+
* @param outputBuffer {Float32Array} the output buffer to write to
|
|
15
|
+
* @param interpolation {interpolationTypes} the interpolation type
|
|
16
|
+
*/
|
|
17
|
+
static getSample(voice, outputBuffer, interpolation)
|
|
18
|
+
{
|
|
19
|
+
const step = voice.currentTuningCalculated * voice.sample.playbackStep;
|
|
20
|
+
// why not?
|
|
21
|
+
if (step === 1)
|
|
22
|
+
{
|
|
23
|
+
WavetableOscillator.getSampleNearest(voice, outputBuffer, step);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
switch (interpolation)
|
|
27
|
+
{
|
|
28
|
+
case interpolationTypes.fourthOrder:
|
|
29
|
+
this.getSampleHermite(voice, outputBuffer, step);
|
|
30
|
+
return;
|
|
31
|
+
|
|
32
|
+
case interpolationTypes.linear:
|
|
33
|
+
default:
|
|
34
|
+
this.getSampleLinear(voice, outputBuffer, step);
|
|
35
|
+
return;
|
|
36
|
+
|
|
37
|
+
case interpolationTypes.nearestNeighbor:
|
|
38
|
+
WavetableOscillator.getSampleNearest(voice, outputBuffer, step);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
9
42
|
|
|
10
43
|
/**
|
|
11
44
|
* Fills the output buffer with raw sample data using linear interpolation
|
|
12
45
|
* @param voice {Voice} the voice we're working on
|
|
13
46
|
* @param outputBuffer {Float32Array} the output buffer to write to
|
|
47
|
+
* @param step {number} the step to advance every sample
|
|
14
48
|
*/
|
|
15
|
-
static getSampleLinear(voice, outputBuffer)
|
|
49
|
+
static getSampleLinear(voice, outputBuffer, step)
|
|
16
50
|
{
|
|
17
51
|
const sample = voice.sample;
|
|
18
52
|
let cur = sample.cursor;
|
|
@@ -45,15 +79,11 @@ export class WavetableOscillator
|
|
|
45
79
|
const lower = sampleData[floor];
|
|
46
80
|
outputBuffer[i] = (lower + (upper - lower) * fraction);
|
|
47
81
|
|
|
48
|
-
cur +=
|
|
82
|
+
cur += step;
|
|
49
83
|
}
|
|
50
84
|
}
|
|
51
85
|
else
|
|
52
86
|
{
|
|
53
|
-
if (sample.loopingMode === 2 && !voice.isInRelease)
|
|
54
|
-
{
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
87
|
for (let i = 0; i < outputBuffer.length; i++)
|
|
58
88
|
{
|
|
59
89
|
|
|
@@ -75,7 +105,7 @@ export class WavetableOscillator
|
|
|
75
105
|
const lower = sampleData[floor];
|
|
76
106
|
outputBuffer[i] = (lower + (upper - lower) * fraction);
|
|
77
107
|
|
|
78
|
-
cur +=
|
|
108
|
+
cur += step;
|
|
79
109
|
}
|
|
80
110
|
}
|
|
81
111
|
voice.sample.cursor = cur;
|
|
@@ -85,15 +115,17 @@ export class WavetableOscillator
|
|
|
85
115
|
* Fills the output buffer with raw sample data using no interpolation (nearest neighbor)
|
|
86
116
|
* @param voice {Voice} the voice we're working on
|
|
87
117
|
* @param outputBuffer {Float32Array} the output buffer to write to
|
|
118
|
+
* @param step {number} the step to advance every sample
|
|
88
119
|
*/
|
|
89
|
-
static getSampleNearest(voice, outputBuffer)
|
|
120
|
+
static getSampleNearest(voice, outputBuffer, step)
|
|
90
121
|
{
|
|
91
122
|
const sample = voice.sample;
|
|
92
123
|
let cur = sample.cursor;
|
|
93
|
-
const loopLength = sample.loopEnd - sample.loopStart;
|
|
94
124
|
const sampleData = sample.sampleData;
|
|
95
|
-
|
|
125
|
+
|
|
126
|
+
if (sample.isLooping)
|
|
96
127
|
{
|
|
128
|
+
const loopLength = sample.loopEnd - sample.loopStart;
|
|
97
129
|
for (let i = 0; i < outputBuffer.length; i++)
|
|
98
130
|
{
|
|
99
131
|
// check for loop
|
|
@@ -111,15 +143,11 @@ export class WavetableOscillator
|
|
|
111
143
|
}
|
|
112
144
|
|
|
113
145
|
outputBuffer[i] = sampleData[ceil];
|
|
114
|
-
cur +=
|
|
146
|
+
cur += step;
|
|
115
147
|
}
|
|
116
148
|
}
|
|
117
149
|
else
|
|
118
150
|
{
|
|
119
|
-
if (sample.loopingMode === 2 && !voice.isInRelease)
|
|
120
|
-
{
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
151
|
for (let i = 0; i < outputBuffer.length; i++)
|
|
124
152
|
{
|
|
125
153
|
|
|
@@ -133,9 +161,8 @@ export class WavetableOscillator
|
|
|
133
161
|
return;
|
|
134
162
|
}
|
|
135
163
|
|
|
136
|
-
//nearest neighbor (uncomment to use)
|
|
137
164
|
outputBuffer[i] = sampleData[ceil];
|
|
138
|
-
cur +=
|
|
165
|
+
cur += step;
|
|
139
166
|
}
|
|
140
167
|
}
|
|
141
168
|
sample.cursor = cur;
|
|
@@ -143,11 +170,12 @@ export class WavetableOscillator
|
|
|
143
170
|
|
|
144
171
|
|
|
145
172
|
/**
|
|
146
|
-
* Fills the output buffer with raw sample data using
|
|
173
|
+
* Fills the output buffer with raw sample data using Hermite interpolation
|
|
147
174
|
* @param voice {Voice} the voice we're working on
|
|
148
175
|
* @param outputBuffer {Float32Array} the output buffer to write to
|
|
176
|
+
* @param step {number} the step to advance every sample
|
|
149
177
|
*/
|
|
150
|
-
static
|
|
178
|
+
static getSampleHermite(voice, outputBuffer, step)
|
|
151
179
|
{
|
|
152
180
|
const sample = voice.sample;
|
|
153
181
|
let cur = sample.cursor;
|
|
@@ -158,21 +186,18 @@ export class WavetableOscillator
|
|
|
158
186
|
const loopLength = sample.loopEnd - sample.loopStart;
|
|
159
187
|
for (let i = 0; i < outputBuffer.length; i++)
|
|
160
188
|
{
|
|
161
|
-
// check for loop
|
|
189
|
+
// check for loop (it can exceed the end point multiple times)
|
|
162
190
|
while (cur >= sample.loopEnd)
|
|
163
191
|
{
|
|
164
192
|
cur -= loopLength;
|
|
165
193
|
}
|
|
166
194
|
|
|
167
|
-
// math comes from
|
|
168
|
-
// https://stackoverflow.com/questions/1125666/how-do-you-do-bicubic-or-other-non-linear-interpolation-of-re-sampled-audio-da
|
|
169
|
-
|
|
170
195
|
// grab the 4 points
|
|
171
|
-
const y0 = ~~cur; // point before the cursor. twice bitwise
|
|
196
|
+
const y0 = ~~cur; // point before the cursor. twice bitwise-not is just a faster Math.floor
|
|
172
197
|
let y1 = y0 + 1; // point after the cursor
|
|
173
|
-
let y2 =
|
|
174
|
-
let y3 =
|
|
175
|
-
const t = cur - y0; // the distance from y0 to cursor
|
|
198
|
+
let y2 = y0 + 2; // point 1 after the cursor
|
|
199
|
+
let y3 = y0 + 3; // point 2 after the cursor
|
|
200
|
+
const t = cur - y0; // the distance from y0 to cursor [0;1]
|
|
176
201
|
// y0 is not handled here
|
|
177
202
|
// as it's math.floor of cur which is handled above
|
|
178
203
|
if (y1 >= sample.loopEnd)
|
|
@@ -189,40 +214,33 @@ export class WavetableOscillator
|
|
|
189
214
|
}
|
|
190
215
|
|
|
191
216
|
// grab the samples
|
|
192
|
-
const
|
|
193
|
-
const
|
|
194
|
-
const
|
|
195
|
-
const
|
|
217
|
+
const xm1 = sampleData[y0];
|
|
218
|
+
const x0 = sampleData[y1];
|
|
219
|
+
const x1 = sampleData[y2];
|
|
220
|
+
const x2 = sampleData[y3];
|
|
196
221
|
|
|
197
222
|
// interpolate
|
|
198
|
-
//
|
|
199
|
-
const
|
|
200
|
-
const
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
223
|
+
// https://www.musicdsp.org/en/latest/Other/93-hermite-interpollation.html
|
|
224
|
+
const c = (x1 - xm1) * 0.5;
|
|
225
|
+
const v = x0 - x1;
|
|
226
|
+
const w = c + v;
|
|
227
|
+
const a = w + v + (x2 - x0) * 0.5;
|
|
228
|
+
const b = w + a;
|
|
229
|
+
outputBuffer[i] = ((((a * t) - b) * t + c) * t + x0);
|
|
204
230
|
|
|
205
|
-
cur +=
|
|
231
|
+
cur += step;
|
|
206
232
|
}
|
|
207
233
|
}
|
|
208
234
|
else
|
|
209
235
|
{
|
|
210
|
-
if (sample.loopingMode === 2 && !voice.isInRelease)
|
|
211
|
-
{
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
236
|
for (let i = 0; i < outputBuffer.length; i++)
|
|
215
237
|
{
|
|
216
|
-
|
|
217
|
-
// math comes from
|
|
218
|
-
// https://stackoverflow.com/questions/1125666/how-do-you-do-bicubic-or-other-non-linear-interpolation-of-re-sampled-audio-da
|
|
219
|
-
|
|
220
238
|
// grab the 4 points
|
|
221
|
-
const y0 = ~~cur; // point before the cursor. twice bitwise
|
|
239
|
+
const y0 = ~~cur; // point before the cursor. twice bitwise-not is just a faster Math.floor
|
|
222
240
|
let y1 = y0 + 1; // point after the cursor
|
|
223
|
-
let y2 =
|
|
224
|
-
let y3 =
|
|
225
|
-
const t = cur - y0; // distance from y0 to cursor
|
|
241
|
+
let y2 = y0 + 2; // point 1 after the cursor
|
|
242
|
+
let y3 = y0 + 3; // point 2 after the cursor
|
|
243
|
+
const t = cur - y0; // the distance from y0 to cursor [0;1]
|
|
226
244
|
|
|
227
245
|
// flag as finished if needed
|
|
228
246
|
if (y1 >= sample.end ||
|
|
@@ -234,18 +252,21 @@ export class WavetableOscillator
|
|
|
234
252
|
}
|
|
235
253
|
|
|
236
254
|
// grab the samples
|
|
237
|
-
const
|
|
238
|
-
const
|
|
239
|
-
const
|
|
240
|
-
const
|
|
255
|
+
const xm1 = sampleData[y0];
|
|
256
|
+
const x0 = sampleData[y1];
|
|
257
|
+
const x1 = sampleData[y2];
|
|
258
|
+
const x2 = sampleData[y3];
|
|
241
259
|
|
|
242
260
|
// interpolate
|
|
243
|
-
|
|
244
|
-
const
|
|
245
|
-
const
|
|
246
|
-
|
|
261
|
+
// https://www.musicdsp.org/en/latest/Other/93-hermite-interpollation.html
|
|
262
|
+
const c = (x1 - xm1) * 0.5;
|
|
263
|
+
const v = x0 - x1;
|
|
264
|
+
const w = c + v;
|
|
265
|
+
const a = w + v + (x2 - x0) * 0.5;
|
|
266
|
+
const b = w + a;
|
|
267
|
+
outputBuffer[i] = ((((a * t) - b) * t + c) * t + x0);
|
|
247
268
|
|
|
248
|
-
cur +=
|
|
269
|
+
cur += step;
|
|
249
270
|
}
|
|
250
271
|
}
|
|
251
272
|
voice.sample.cursor = cur;
|
|
@@ -5,7 +5,6 @@ import { absCentsToHz, timecentsToSeconds } from "../engine_components/unit_conv
|
|
|
5
5
|
import { getLFOValue } from "../engine_components/lfo.js";
|
|
6
6
|
import { WavetableOscillator } from "../engine_components/wavetable_oscillator.js";
|
|
7
7
|
import { LowpassFilter } from "../engine_components/lowpass_filter.js";
|
|
8
|
-
import { interpolationTypes } from "../engine_components/enums.js";
|
|
9
8
|
import { generatorTypes } from "../../../soundfont/basic_soundfont/generator_types.js";
|
|
10
9
|
|
|
11
10
|
/**
|
|
@@ -173,23 +172,22 @@ export function renderVoice(
|
|
|
173
172
|
// SYNTHESIS
|
|
174
173
|
const bufferOut = new Float32Array(sampleCount);
|
|
175
174
|
|
|
176
|
-
|
|
177
|
-
|
|
175
|
+
|
|
176
|
+
// looping mode 2: start on release. process only volEnv
|
|
177
|
+
if (voice.sample.loopingMode === 2 && !voice.isInRelease)
|
|
178
178
|
{
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
break;
|
|
187
|
-
|
|
188
|
-
case interpolationTypes.nearestNeighbor:
|
|
189
|
-
WavetableOscillator.getSampleNearest(voice, bufferOut);
|
|
190
|
-
break;
|
|
179
|
+
VolumeEnvelope.apply(
|
|
180
|
+
voice,
|
|
181
|
+
bufferOut,
|
|
182
|
+
volumeExcursionCentibels,
|
|
183
|
+
this.synth.volumeEnvelopeSmoothingFactor
|
|
184
|
+
);
|
|
185
|
+
return voice.finished;
|
|
191
186
|
}
|
|
192
187
|
|
|
188
|
+
// wave table oscillator
|
|
189
|
+
WavetableOscillator.getSample(voice, bufferOut, this.synth.interpolationType);
|
|
190
|
+
|
|
193
191
|
// low pass filter
|
|
194
192
|
LowpassFilter.apply(voice, bufferOut, lowpassExcursion, this.synth.filterSmoothingFactor);
|
|
195
193
|
|