spessasynth_lib 3.24.13 → 3.24.16
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 +457 -68
- package/midi_parser/midi_loader.js +18 -503
- package/midi_parser/midi_message.js +18 -5
- package/midi_parser/midi_sequence.js +2 -2
- package/package.json +1 -1
- package/sequencer/worklet_sequencer/process_event.js +1 -6
- package/synthetizer/synthetizer.js +13 -7
- package/synthetizer/worklet_processor.min.js +12 -12
- package/synthetizer/worklet_system/README.md +2 -2
- package/synthetizer/worklet_system/main_processor.js +106 -95
- package/synthetizer/worklet_system/message_protocol/handle_message.js +22 -17
- package/synthetizer/worklet_system/message_protocol/worklet_message.js +2 -1
- package/synthetizer/worklet_system/snapshot/apply_synthesizer_snapshot.js +14 -0
- package/synthetizer/worklet_system/snapshot/channel_snapshot.js +166 -0
- package/synthetizer/worklet_system/snapshot/send_synthesizer_snapshot.js +14 -0
- package/synthetizer/worklet_system/snapshot/synthesizer_snapshot.js +121 -0
- package/synthetizer/worklet_system/worklet_methods/controller_control/controller_change.js +196 -0
- package/synthetizer/worklet_system/worklet_methods/controller_control/master_parameters.js +34 -0
- package/synthetizer/worklet_system/worklet_methods/{reset_controllers.js → controller_control/reset_controllers.js} +33 -39
- package/synthetizer/worklet_system/worklet_methods/create_worklet_channel.js +26 -0
- package/synthetizer/worklet_system/worklet_methods/{data_entry.js → data_entry/data_entry_coarse.js} +38 -105
- package/synthetizer/worklet_system/worklet_methods/data_entry/data_entry_fine.js +64 -0
- package/synthetizer/worklet_system/worklet_methods/mute_channel.js +17 -0
- package/synthetizer/worklet_system/worklet_methods/note_on.js +36 -34
- package/synthetizer/worklet_system/worklet_methods/program_change.js +49 -0
- package/synthetizer/worklet_system/worklet_methods/{voice_control.js → render_voice.js} +37 -120
- package/synthetizer/worklet_system/worklet_methods/soundfont_management/clear_sound_font.js +35 -0
- package/synthetizer/worklet_system/worklet_methods/soundfont_management/get_preset.js +20 -0
- package/synthetizer/worklet_system/worklet_methods/soundfont_management/reload_sound_font.js +43 -0
- package/synthetizer/worklet_system/worklet_methods/soundfont_management/send_preset_list.js +31 -0
- package/synthetizer/worklet_system/worklet_methods/soundfont_management/set_embedded_sound_font.js +21 -0
- package/synthetizer/worklet_system/worklet_methods/stopping_notes/kill_note.js +20 -0
- package/synthetizer/worklet_system/worklet_methods/stopping_notes/note_off.js +55 -0
- package/synthetizer/worklet_system/worklet_methods/stopping_notes/stop_all_channels.js +16 -0
- package/synthetizer/worklet_system/worklet_methods/stopping_notes/stop_all_notes.js +30 -0
- package/synthetizer/worklet_system/worklet_methods/stopping_notes/voice_killing.js +63 -0
- package/synthetizer/worklet_system/worklet_methods/system_exclusive.js +31 -30
- package/synthetizer/worklet_system/worklet_methods/tuning_control/channel_pressure.js +24 -0
- package/synthetizer/worklet_system/worklet_methods/tuning_control/pitch_wheel.js +33 -0
- package/synthetizer/worklet_system/worklet_methods/tuning_control/poly_pressure.js +31 -0
- package/synthetizer/worklet_system/worklet_methods/tuning_control/set_master_tuning.js +15 -0
- package/synthetizer/worklet_system/worklet_methods/tuning_control/set_modulation_depth.js +27 -0
- package/synthetizer/worklet_system/worklet_methods/tuning_control/set_octave_tuning.js +15 -0
- package/synthetizer/worklet_system/worklet_methods/tuning_control/set_tuning.js +24 -0
- package/synthetizer/worklet_system/worklet_methods/tuning_control/set_tuning_semitones.js +19 -0
- package/synthetizer/worklet_system/worklet_methods/tuning_control/transpose_all_channels.js +15 -0
- package/synthetizer/worklet_system/worklet_methods/tuning_control/transpose_channel.js +31 -0
- package/synthetizer/worklet_system/worklet_utilities/controller_tables.js +10 -1
- package/synthetizer/worklet_system/worklet_utilities/lfo.js +2 -1
- package/synthetizer/worklet_system/worklet_utilities/modulation_envelope.js +4 -4
- package/synthetizer/worklet_system/worklet_utilities/modulator_curves.js +4 -5
- package/synthetizer/worklet_system/worklet_utilities/stereo_panner.js +18 -18
- package/synthetizer/worklet_system/worklet_utilities/wavetable_oscillator.js +210 -206
- package/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +354 -108
- package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +22 -9
- package/synthetizer/worklet_system/snapshot/snapshot.js +0 -311
- package/synthetizer/worklet_system/worklet_methods/controller_control.js +0 -260
- package/synthetizer/worklet_system/worklet_methods/note_off.js +0 -119
- package/synthetizer/worklet_system/worklet_methods/program_control.js +0 -282
- package/synthetizer/worklet_system/worklet_methods/tuning_control.js +0 -233
- package/synthetizer/worklet_system/worklet_methods/vibrato_control.js +0 -29
|
@@ -14,246 +14,250 @@ export const interpolationTypes = {
|
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
* Fills the output buffer with raw sample data using linear interpolation
|
|
19
|
-
* @param voice {WorkletVoice} the voice we're working on
|
|
20
|
-
* @param outputBuffer {Float32Array} the output buffer to write to
|
|
21
|
-
*/
|
|
22
|
-
export function getSampleLinear(voice, outputBuffer)
|
|
17
|
+
export class WavetableOscillator
|
|
23
18
|
{
|
|
24
|
-
const sample = voice.sample;
|
|
25
|
-
let cur = sample.cursor;
|
|
26
|
-
const sampleData = sample.sampleData;
|
|
27
19
|
|
|
28
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Fills the output buffer with raw sample data using linear interpolation
|
|
22
|
+
* @param voice {WorkletVoice} the voice we're working on
|
|
23
|
+
* @param outputBuffer {Float32Array} the output buffer to write to
|
|
24
|
+
*/
|
|
25
|
+
static getSampleLinear(voice, outputBuffer)
|
|
29
26
|
{
|
|
30
|
-
const
|
|
31
|
-
|
|
27
|
+
const sample = voice.sample;
|
|
28
|
+
let cur = sample.cursor;
|
|
29
|
+
const sampleData = sample.sampleData;
|
|
30
|
+
|
|
31
|
+
if (sample.isLooping)
|
|
32
32
|
{
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
const loopLength = sample.loopEnd - sample.loopStart;
|
|
34
|
+
for (let i = 0; i < outputBuffer.length; i++)
|
|
35
35
|
{
|
|
36
|
-
|
|
36
|
+
// check for loop
|
|
37
|
+
while (cur >= sample.loopEnd)
|
|
38
|
+
{
|
|
39
|
+
cur -= loopLength;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// grab the 2 nearest points
|
|
43
|
+
const floor = ~~cur;
|
|
44
|
+
let ceil = floor + 1;
|
|
45
|
+
|
|
46
|
+
while (ceil >= sample.loopEnd)
|
|
47
|
+
{
|
|
48
|
+
ceil -= loopLength;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const fraction = cur - floor;
|
|
52
|
+
|
|
53
|
+
// grab the samples and interpolate
|
|
54
|
+
const upper = sampleData[ceil];
|
|
55
|
+
const lower = sampleData[floor];
|
|
56
|
+
outputBuffer[i] = (lower + (upper - lower) * fraction);
|
|
57
|
+
|
|
58
|
+
cur += sample.playbackStep * voice.currentTuningCalculated;
|
|
37
59
|
}
|
|
38
|
-
|
|
39
|
-
// grab the 2 nearest points
|
|
40
|
-
const floor = ~~cur;
|
|
41
|
-
let ceil = floor + 1;
|
|
42
|
-
|
|
43
|
-
while (ceil >= sample.loopEnd)
|
|
44
|
-
{
|
|
45
|
-
ceil -= loopLength;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const fraction = cur - floor;
|
|
49
|
-
|
|
50
|
-
// grab the samples and interpolate
|
|
51
|
-
const upper = sampleData[ceil];
|
|
52
|
-
const lower = sampleData[floor];
|
|
53
|
-
outputBuffer[i] = (lower + (upper - lower) * fraction);
|
|
54
|
-
|
|
55
|
-
cur += sample.playbackStep * voice.currentTuningCalculated;
|
|
56
60
|
}
|
|
57
|
-
|
|
58
|
-
else
|
|
59
|
-
{
|
|
60
|
-
if (sample.loopingMode === 2 && !voice.isInRelease)
|
|
61
|
-
{
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
for (let i = 0; i < outputBuffer.length; i++)
|
|
61
|
+
else
|
|
65
62
|
{
|
|
66
|
-
|
|
67
|
-
// linear interpolation
|
|
68
|
-
const floor = ~~cur;
|
|
69
|
-
const ceil = floor + 1;
|
|
70
|
-
|
|
71
|
-
// flag the voice as finished if needed
|
|
72
|
-
if (ceil >= sample.end)
|
|
63
|
+
if (sample.loopingMode === 2 && !voice.isInRelease)
|
|
73
64
|
{
|
|
74
|
-
voice.finished = true;
|
|
75
65
|
return;
|
|
76
66
|
}
|
|
77
|
-
|
|
78
|
-
const fraction = cur - floor;
|
|
79
|
-
|
|
80
|
-
// grab the samples and interpolate
|
|
81
|
-
const upper = sampleData[ceil];
|
|
82
|
-
const lower = sampleData[floor];
|
|
83
|
-
outputBuffer[i] = (lower + (upper - lower) * fraction);
|
|
84
|
-
|
|
85
|
-
cur += sample.playbackStep * voice.currentTuningCalculated;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
voice.sample.cursor = cur;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Fills the output buffer with raw sample data using no interpolation (nearest neighbor)
|
|
93
|
-
* @param voice {WorkletVoice} the voice we're working on
|
|
94
|
-
* @param outputBuffer {Float32Array} the output buffer to write to
|
|
95
|
-
*/
|
|
96
|
-
export function getSampleNearest(voice, outputBuffer)
|
|
97
|
-
{
|
|
98
|
-
const sample = voice.sample;
|
|
99
|
-
let cur = sample.cursor;
|
|
100
|
-
const loopLength = sample.loopEnd - sample.loopStart;
|
|
101
|
-
const sampleData = sample.sampleData;
|
|
102
|
-
if (voice.sample.isLooping)
|
|
103
|
-
{
|
|
104
|
-
for (let i = 0; i < outputBuffer.length; i++)
|
|
105
|
-
{
|
|
106
|
-
// check for loop
|
|
107
|
-
while (cur >= sample.loopEnd)
|
|
108
|
-
{
|
|
109
|
-
cur -= loopLength;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// grab the nearest neighbor
|
|
113
|
-
let ceil = ~~cur + 1;
|
|
114
|
-
|
|
115
|
-
while (ceil >= sample.loopEnd)
|
|
116
|
-
{
|
|
117
|
-
ceil -= loopLength;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
outputBuffer[i] = sampleData[ceil];
|
|
121
|
-
cur += sample.playbackStep * voice.currentTuningCalculated;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
else
|
|
125
|
-
{
|
|
126
|
-
if (sample.loopingMode === 2 && !voice.isInRelease)
|
|
127
|
-
{
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
for (let i = 0; i < outputBuffer.length; i++)
|
|
131
|
-
{
|
|
132
|
-
|
|
133
|
-
// nearest neighbor
|
|
134
|
-
const ceil = ~~cur + 1;
|
|
135
|
-
|
|
136
|
-
// flag the voice as finished if needed
|
|
137
|
-
if (ceil >= sample.end)
|
|
67
|
+
for (let i = 0; i < outputBuffer.length; i++)
|
|
138
68
|
{
|
|
139
|
-
|
|
140
|
-
|
|
69
|
+
|
|
70
|
+
// linear interpolation
|
|
71
|
+
const floor = ~~cur;
|
|
72
|
+
const ceil = floor + 1;
|
|
73
|
+
|
|
74
|
+
// flag the voice as finished if needed
|
|
75
|
+
if (ceil >= sample.end)
|
|
76
|
+
{
|
|
77
|
+
voice.finished = true;
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const fraction = cur - floor;
|
|
82
|
+
|
|
83
|
+
// grab the samples and interpolate
|
|
84
|
+
const upper = sampleData[ceil];
|
|
85
|
+
const lower = sampleData[floor];
|
|
86
|
+
outputBuffer[i] = (lower + (upper - lower) * fraction);
|
|
87
|
+
|
|
88
|
+
cur += sample.playbackStep * voice.currentTuningCalculated;
|
|
141
89
|
}
|
|
142
|
-
|
|
143
|
-
//nearest neighbor (uncomment to use)
|
|
144
|
-
outputBuffer[i] = sampleData[ceil];
|
|
145
|
-
cur += sample.playbackStep * voice.currentTuningCalculated;
|
|
146
90
|
}
|
|
91
|
+
voice.sample.cursor = cur;
|
|
147
92
|
}
|
|
148
|
-
sample.cursor = cur;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Fills the output buffer with raw sample data using cubic interpolation
|
|
154
|
-
* @param voice {WorkletVoice} the voice we're working on
|
|
155
|
-
* @param outputBuffer {Float32Array} the output buffer to write to
|
|
156
|
-
*/
|
|
157
|
-
export function getSampleCubic(voice, outputBuffer)
|
|
158
|
-
{
|
|
159
|
-
const sample = voice.sample;
|
|
160
|
-
let cur = sample.cursor;
|
|
161
|
-
const sampleData = sample.sampleData;
|
|
162
93
|
|
|
163
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Fills the output buffer with raw sample data using no interpolation (nearest neighbor)
|
|
96
|
+
* @param voice {WorkletVoice} the voice we're working on
|
|
97
|
+
* @param outputBuffer {Float32Array} the output buffer to write to
|
|
98
|
+
*/
|
|
99
|
+
static getSampleNearest(voice, outputBuffer)
|
|
164
100
|
{
|
|
101
|
+
const sample = voice.sample;
|
|
102
|
+
let cur = sample.cursor;
|
|
165
103
|
const loopLength = sample.loopEnd - sample.loopStart;
|
|
166
|
-
|
|
104
|
+
const sampleData = sample.sampleData;
|
|
105
|
+
if (voice.sample.isLooping)
|
|
167
106
|
{
|
|
168
|
-
|
|
169
|
-
while (cur >= sample.loopEnd)
|
|
107
|
+
for (let i = 0; i < outputBuffer.length; i++)
|
|
170
108
|
{
|
|
171
|
-
|
|
109
|
+
// check for loop
|
|
110
|
+
while (cur >= sample.loopEnd)
|
|
111
|
+
{
|
|
112
|
+
cur -= loopLength;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// grab the nearest neighbor
|
|
116
|
+
let ceil = ~~cur + 1;
|
|
117
|
+
|
|
118
|
+
while (ceil >= sample.loopEnd)
|
|
119
|
+
{
|
|
120
|
+
ceil -= loopLength;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
outputBuffer[i] = sampleData[ceil];
|
|
124
|
+
cur += sample.playbackStep * voice.currentTuningCalculated;
|
|
172
125
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
// grab the 4 points
|
|
178
|
-
const y0 = ~~cur; // point before the cursor. twice bitwise not is just a faster Math.floor
|
|
179
|
-
let y1 = y0 + 1; // point after the cursor
|
|
180
|
-
let y2 = y1 + 1; // point 1 after the cursor
|
|
181
|
-
let y3 = y2 + 1; // point 2 after the cursor
|
|
182
|
-
const t = cur - y0; // distance from y0 to cursor
|
|
183
|
-
// y0 is not handled here
|
|
184
|
-
// as it's math.floor of cur which is handled above
|
|
185
|
-
if (y1 >= sample.loopEnd)
|
|
186
|
-
{
|
|
187
|
-
y1 -= loopLength;
|
|
188
|
-
}
|
|
189
|
-
if (y2 >= sample.loopEnd)
|
|
126
|
+
}
|
|
127
|
+
else
|
|
128
|
+
{
|
|
129
|
+
if (sample.loopingMode === 2 && !voice.isInRelease)
|
|
190
130
|
{
|
|
191
|
-
|
|
131
|
+
return;
|
|
192
132
|
}
|
|
193
|
-
|
|
133
|
+
for (let i = 0; i < outputBuffer.length; i++)
|
|
194
134
|
{
|
|
195
|
-
|
|
135
|
+
|
|
136
|
+
// nearest neighbor
|
|
137
|
+
const ceil = ~~cur + 1;
|
|
138
|
+
|
|
139
|
+
// flag the voice as finished if needed
|
|
140
|
+
if (ceil >= sample.end)
|
|
141
|
+
{
|
|
142
|
+
voice.finished = true;
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
//nearest neighbor (uncomment to use)
|
|
147
|
+
outputBuffer[i] = sampleData[ceil];
|
|
148
|
+
cur += sample.playbackStep * voice.currentTuningCalculated;
|
|
196
149
|
}
|
|
197
|
-
|
|
198
|
-
// grab the samples
|
|
199
|
-
const x0 = sampleData[y0];
|
|
200
|
-
const x1 = sampleData[y1];
|
|
201
|
-
const x2 = sampleData[y2];
|
|
202
|
-
const x3 = sampleData[y3];
|
|
203
|
-
|
|
204
|
-
// interpolate
|
|
205
|
-
// const c0 = x1
|
|
206
|
-
const c1 = 0.5 * (x2 - x0);
|
|
207
|
-
const c2 = x0 - (2.5 * x1) + (2 * x2) - (0.5 * x3);
|
|
208
|
-
const c3 = (0.5 * (x3 - x0)) + (1.5 * (x1 - x2));
|
|
209
|
-
outputBuffer[i] = (((((c3 * t) + c2) * t) + c1) * t) + x1;
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
cur += sample.playbackStep * voice.currentTuningCalculated;
|
|
213
150
|
}
|
|
151
|
+
sample.cursor = cur;
|
|
214
152
|
}
|
|
215
|
-
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Fills the output buffer with raw sample data using cubic interpolation
|
|
157
|
+
* @param voice {WorkletVoice} the voice we're working on
|
|
158
|
+
* @param outputBuffer {Float32Array} the output buffer to write to
|
|
159
|
+
*/
|
|
160
|
+
static getSampleCubic(voice, outputBuffer)
|
|
216
161
|
{
|
|
217
|
-
|
|
162
|
+
const sample = voice.sample;
|
|
163
|
+
let cur = sample.cursor;
|
|
164
|
+
const sampleData = sample.sampleData;
|
|
165
|
+
|
|
166
|
+
if (sample.isLooping)
|
|
218
167
|
{
|
|
219
|
-
|
|
168
|
+
const loopLength = sample.loopEnd - sample.loopStart;
|
|
169
|
+
for (let i = 0; i < outputBuffer.length; i++)
|
|
170
|
+
{
|
|
171
|
+
// check for loop
|
|
172
|
+
while (cur >= sample.loopEnd)
|
|
173
|
+
{
|
|
174
|
+
cur -= loopLength;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// math comes from
|
|
178
|
+
// https://stackoverflow.com/questions/1125666/how-do-you-do-bicubic-or-other-non-linear-interpolation-of-re-sampled-audio-da
|
|
179
|
+
|
|
180
|
+
// grab the 4 points
|
|
181
|
+
const y0 = ~~cur; // point before the cursor. twice bitwise not is just a faster Math.floor
|
|
182
|
+
let y1 = y0 + 1; // point after the cursor
|
|
183
|
+
let y2 = y1 + 1; // point 1 after the cursor
|
|
184
|
+
let y3 = y2 + 1; // point 2 after the cursor
|
|
185
|
+
const t = cur - y0; // distance from y0 to cursor
|
|
186
|
+
// y0 is not handled here
|
|
187
|
+
// as it's math.floor of cur which is handled above
|
|
188
|
+
if (y1 >= sample.loopEnd)
|
|
189
|
+
{
|
|
190
|
+
y1 -= loopLength;
|
|
191
|
+
}
|
|
192
|
+
if (y2 >= sample.loopEnd)
|
|
193
|
+
{
|
|
194
|
+
y2 -= loopLength;
|
|
195
|
+
}
|
|
196
|
+
if (y3 >= sample.loopEnd)
|
|
197
|
+
{
|
|
198
|
+
y3 -= loopLength;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// grab the samples
|
|
202
|
+
const x0 = sampleData[y0];
|
|
203
|
+
const x1 = sampleData[y1];
|
|
204
|
+
const x2 = sampleData[y2];
|
|
205
|
+
const x3 = sampleData[y3];
|
|
206
|
+
|
|
207
|
+
// interpolate
|
|
208
|
+
// const c0 = x1
|
|
209
|
+
const c1 = 0.5 * (x2 - x0);
|
|
210
|
+
const c2 = x0 - (2.5 * x1) + (2 * x2) - (0.5 * x3);
|
|
211
|
+
const c3 = (0.5 * (x3 - x0)) + (1.5 * (x1 - x2));
|
|
212
|
+
outputBuffer[i] = (((((c3 * t) + c2) * t) + c1) * t) + x1;
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
cur += sample.playbackStep * voice.currentTuningCalculated;
|
|
216
|
+
}
|
|
220
217
|
}
|
|
221
|
-
|
|
218
|
+
else
|
|
222
219
|
{
|
|
223
|
-
|
|
224
|
-
// math comes from
|
|
225
|
-
// https://stackoverflow.com/questions/1125666/how-do-you-do-bicubic-or-other-non-linear-interpolation-of-re-sampled-audio-da
|
|
226
|
-
|
|
227
|
-
// grab the 4 points
|
|
228
|
-
const y0 = ~~cur; // point before the cursor. twice bitwise not is just a faster Math.floor
|
|
229
|
-
let y1 = y0 + 1; // point after the cursor
|
|
230
|
-
let y2 = y1 + 1; // point 1 after the cursor
|
|
231
|
-
let y3 = y2 + 1; // point 2 after the cursor
|
|
232
|
-
const t = cur - y0; // distance from y0 to cursor
|
|
233
|
-
|
|
234
|
-
// flag as finished if needed
|
|
235
|
-
if (y1 >= sample.end ||
|
|
236
|
-
y2 >= sample.end ||
|
|
237
|
-
y3 >= sample.end)
|
|
220
|
+
if (sample.loopingMode === 2 && !voice.isInRelease)
|
|
238
221
|
{
|
|
239
|
-
voice.finished = true;
|
|
240
222
|
return;
|
|
241
223
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
224
|
+
for (let i = 0; i < outputBuffer.length; i++)
|
|
225
|
+
{
|
|
226
|
+
|
|
227
|
+
// math comes from
|
|
228
|
+
// https://stackoverflow.com/questions/1125666/how-do-you-do-bicubic-or-other-non-linear-interpolation-of-re-sampled-audio-da
|
|
229
|
+
|
|
230
|
+
// grab the 4 points
|
|
231
|
+
const y0 = ~~cur; // point before the cursor. twice bitwise not is just a faster Math.floor
|
|
232
|
+
let y1 = y0 + 1; // point after the cursor
|
|
233
|
+
let y2 = y1 + 1; // point 1 after the cursor
|
|
234
|
+
let y3 = y2 + 1; // point 2 after the cursor
|
|
235
|
+
const t = cur - y0; // distance from y0 to cursor
|
|
236
|
+
|
|
237
|
+
// flag as finished if needed
|
|
238
|
+
if (y1 >= sample.end ||
|
|
239
|
+
y2 >= sample.end ||
|
|
240
|
+
y3 >= sample.end)
|
|
241
|
+
{
|
|
242
|
+
voice.finished = true;
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// grab the samples
|
|
247
|
+
const x0 = sampleData[y0];
|
|
248
|
+
const x1 = sampleData[y1];
|
|
249
|
+
const x2 = sampleData[y2];
|
|
250
|
+
const x3 = sampleData[y3];
|
|
251
|
+
|
|
252
|
+
// interpolate
|
|
253
|
+
const c1 = 0.5 * (x2 - x0);
|
|
254
|
+
const c2 = x0 - (2.5 * x1) + (2 * x2) - (0.5 * x3);
|
|
255
|
+
const c3 = (0.5 * (x3 - x0)) + (1.5 * (x1 - x2));
|
|
256
|
+
outputBuffer[i] = (((((c3 * t) + c2) * t) + c1) * t) + x1;
|
|
257
|
+
|
|
258
|
+
cur += sample.playbackStep * voice.currentTuningCalculated;
|
|
259
|
+
}
|
|
256
260
|
}
|
|
261
|
+
voice.sample.cursor = cur;
|
|
257
262
|
}
|
|
258
|
-
voice.sample.cursor = cur;
|
|
259
263
|
}
|