spessasynth_lib 3.20.6 → 3.20.8
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/synthetizer/synthetizer.d.ts +13 -10
- package/@types/synthetizer/worklet_system/message_protocol/worklet_message.d.ts +3 -0
- package/package.json +1 -1
- package/soundfont/dls/read_articulation.js +13 -8
- package/synthetizer/synthetizer.js +42 -15
- package/synthetizer/worklet_processor.min.js +6 -6
- package/synthetizer/worklet_system/main_processor.js +13 -0
- package/synthetizer/worklet_system/message_protocol/handle_message.js +4 -0
- package/synthetizer/worklet_system/message_protocol/worklet_message.js +3 -0
- package/synthetizer/worklet_system/worklet_methods/voice_control.js +9 -2
- package/synthetizer/worklet_system/worklet_utilities/wavetable_oscillator.js +73 -2
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
} from './worklet_methods/program_control.js'
|
|
39
39
|
import { applySynthesizerSnapshot, sendSynthesizerSnapshot } from './worklet_methods/snapshot.js'
|
|
40
40
|
import { WorkletSoundfontManager } from './worklet_methods/worklet_soundfont_manager/worklet_soundfont_manager.js'
|
|
41
|
+
import { interpolationTypes } from './worklet_utilities/wavetable_oscillator.js'
|
|
41
42
|
|
|
42
43
|
|
|
43
44
|
/**
|
|
@@ -45,6 +46,8 @@ import { WorkletSoundfontManager } from './worklet_methods/worklet_soundfont_man
|
|
|
45
46
|
* purpose: manages the synthesizer (and worklet sequencer) from the AudioWorkletGlobalScope and renders the audio data
|
|
46
47
|
*/
|
|
47
48
|
|
|
49
|
+
const WORKLET_PROCESSOR_VERSION = "3.20.8";
|
|
50
|
+
|
|
48
51
|
export const MIN_NOTE_LENGTH = 0.07; // if the note is released faster than that, it forced to last that long
|
|
49
52
|
|
|
50
53
|
export const SYNTHESIZER_GAIN = 1.0;
|
|
@@ -79,6 +82,12 @@ class SpessaSynthProcessor extends AudioWorkletProcessor
|
|
|
79
82
|
*/
|
|
80
83
|
this.deviceID = ALL_CHANNELS_OR_DIFFERENT_ACTION;
|
|
81
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Interpolation type used
|
|
87
|
+
* @type {interpolationTypes}
|
|
88
|
+
*/
|
|
89
|
+
this.interpolationType = interpolationTypes.linear;
|
|
90
|
+
|
|
82
91
|
/**
|
|
83
92
|
* @type {function}
|
|
84
93
|
*/
|
|
@@ -218,6 +227,10 @@ class SpessaSynthProcessor extends AudioWorkletProcessor
|
|
|
218
227
|
|
|
219
228
|
stbvorbis.isInitialized.then(() => {
|
|
220
229
|
this.postReady();
|
|
230
|
+
this.post({
|
|
231
|
+
messageType: returnMessageType.identify,
|
|
232
|
+
messageData: WORKLET_PROCESSOR_VERSION
|
|
233
|
+
});
|
|
221
234
|
SpessaSynthInfo("%cSpessaSynth is ready!", consoleColors.recognized);
|
|
222
235
|
});
|
|
223
236
|
}
|
|
@@ -64,6 +64,7 @@ export const masterParameterType = {
|
|
|
64
64
|
mainVolume: 0,
|
|
65
65
|
masterPan: 1,
|
|
66
66
|
voicesCap: 2,
|
|
67
|
+
interpolationType: 3
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
|
|
@@ -106,6 +107,7 @@ export const ALL_CHANNELS_OR_DIFFERENT_ACTION = -1;
|
|
|
106
107
|
* 4 - synthesizer snapshot -> snapshot<SynthesizerSnapshot> note: refer to snapshot.js
|
|
107
108
|
* 5 - ready -> (no data)
|
|
108
109
|
* 6 - soundfontError -> errorMessage<string>
|
|
110
|
+
* 7 - idenfity -> version<string>
|
|
109
111
|
*/
|
|
110
112
|
|
|
111
113
|
/**
|
|
@@ -119,4 +121,5 @@ export const returnMessageType = {
|
|
|
119
121
|
synthesizerSnapshot: 4,
|
|
120
122
|
ready: 5,
|
|
121
123
|
soundfontError: 6,
|
|
124
|
+
identify: 7,
|
|
122
125
|
}
|
|
@@ -3,7 +3,7 @@ import { absCentsToHz, timecentsToSeconds } from '../worklet_utilities/unit_conv
|
|
|
3
3
|
import { getLFOValue } from '../worklet_utilities/lfo.js'
|
|
4
4
|
import { customControllers } from '../worklet_utilities/worklet_processor_channel.js'
|
|
5
5
|
import { WorkletModulationEnvelope } from '../worklet_utilities/modulation_envelope.js'
|
|
6
|
-
import {
|
|
6
|
+
import { getSampleLinear, getSampleNearest, interpolationTypes } from '../worklet_utilities/wavetable_oscillator.js'
|
|
7
7
|
import { panVoice } from '../worklet_utilities/stereo_panner.js'
|
|
8
8
|
import { applyLowpassFilter } from '../worklet_utilities/lowpass_filter.js'
|
|
9
9
|
import { MIN_NOTE_LENGTH } from '../main_processor.js'
|
|
@@ -147,7 +147,14 @@ export function renderVoice(
|
|
|
147
147
|
const bufferOut = new Float32Array(outputLeft.length);
|
|
148
148
|
|
|
149
149
|
// wavetable oscillator
|
|
150
|
-
|
|
150
|
+
if(this.interpolationType === interpolationTypes.linear)
|
|
151
|
+
{
|
|
152
|
+
getSampleLinear(voice, this.workletDumpedSamplesList[voice.sample.sampleID], bufferOut);
|
|
153
|
+
}
|
|
154
|
+
else
|
|
155
|
+
{
|
|
156
|
+
getSampleNearest(voice, this.workletDumpedSamplesList[voice.sample.sampleID], bufferOut);
|
|
157
|
+
}
|
|
151
158
|
|
|
152
159
|
// lowpass filter
|
|
153
160
|
applyLowpassFilter(voice, bufferOut, lowpassCents);
|
|
@@ -3,14 +3,23 @@
|
|
|
3
3
|
* purpose: plays back raw audio data at an arbitrary playback rate
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* @enum {number}
|
|
9
|
+
*/
|
|
10
|
+
export const interpolationTypes = {
|
|
11
|
+
linear: 0,
|
|
12
|
+
nearestNeighbor: 1
|
|
13
|
+
}
|
|
14
|
+
|
|
6
15
|
|
|
7
16
|
/**
|
|
8
|
-
* Fills the output buffer with raw sample data
|
|
17
|
+
* Fills the output buffer with raw sample data using linear interpolation
|
|
9
18
|
* @param voice {WorkletVoice} the voice we're working on
|
|
10
19
|
* @param sampleData {Float32Array} the sample data to write with
|
|
11
20
|
* @param outputBuffer {Float32Array} the output buffer to write to
|
|
12
21
|
*/
|
|
13
|
-
export function
|
|
22
|
+
export function getSampleLinear(voice, sampleData, outputBuffer)
|
|
14
23
|
{
|
|
15
24
|
let cur = voice.sample.cursor;
|
|
16
25
|
const loop = (voice.sample.loopingMode === 1) || (voice.sample.loopingMode === 3 && !voice.isInRelease);
|
|
@@ -93,4 +102,66 @@ export function getOscillatorData(voice, sampleData, outputBuffer)
|
|
|
93
102
|
}
|
|
94
103
|
}
|
|
95
104
|
voice.sample.cursor = cur;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Fills the output buffer with raw sample data using no interpolation (nearest neighbor)
|
|
109
|
+
* @param voice {WorkletVoice} the voice we're working on
|
|
110
|
+
* @param sampleData {Float32Array} the sample data to write with
|
|
111
|
+
* @param outputBuffer {Float32Array} the output buffer to write to
|
|
112
|
+
*/
|
|
113
|
+
export function getSampleNearest(voice, sampleData, outputBuffer)
|
|
114
|
+
{
|
|
115
|
+
let cur = voice.sample.cursor;
|
|
116
|
+
const loop = (voice.sample.loopingMode === 1) || (voice.sample.loopingMode === 3 && !voice.isInRelease);
|
|
117
|
+
const loopLength = voice.sample.loopEnd - voice.sample.loopStart;
|
|
118
|
+
|
|
119
|
+
if(loop)
|
|
120
|
+
{
|
|
121
|
+
for (let i = 0; i < outputBuffer.length; i++)
|
|
122
|
+
{
|
|
123
|
+
// check for loop
|
|
124
|
+
while(cur >= voice.sample.loopEnd)
|
|
125
|
+
{
|
|
126
|
+
cur -= loopLength;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// grab the nearest neighbor
|
|
130
|
+
let ceil = ~~cur + 1;
|
|
131
|
+
|
|
132
|
+
while(ceil >= voice.sample.loopEnd)
|
|
133
|
+
{
|
|
134
|
+
ceil -= loopLength;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
outputBuffer[i] = sampleData[ceil];
|
|
138
|
+
cur += voice.sample.playbackStep * voice.currentTuningCalculated;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else
|
|
142
|
+
{
|
|
143
|
+
// check and correct end errors
|
|
144
|
+
if(voice.sample.end >= sampleData.length)
|
|
145
|
+
{
|
|
146
|
+
voice.sample.end = sampleData.length - 1;
|
|
147
|
+
}
|
|
148
|
+
for (let i = 0; i < outputBuffer.length; i++)
|
|
149
|
+
{
|
|
150
|
+
|
|
151
|
+
// nearest neighbor
|
|
152
|
+
const ceil = ~~cur + 1;
|
|
153
|
+
|
|
154
|
+
// flag the voice as finished if needed
|
|
155
|
+
if(ceil >= voice.sample.end)
|
|
156
|
+
{
|
|
157
|
+
voice.finished = true;
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
//nearest neighbor (uncomment to use)
|
|
162
|
+
outputBuffer[i] = sampleData[ceil];
|
|
163
|
+
cur += voice.sample.playbackStep * voice.currentTuningCalculated;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
voice.sample.cursor = cur;
|
|
96
167
|
}
|