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.
@@ -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
  }
@@ -141,6 +141,10 @@ export function handleMessage(message)
141
141
  case masterParameterType.voicesCap:
142
142
  this.voiceCap = value;
143
143
  break;
144
+
145
+ case masterParameterType.interpolationType:
146
+ this.interpolationType = value;
147
+ break;
144
148
  }
145
149
  break;
146
150
 
@@ -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 { getOscillatorData } from '../worklet_utilities/wavetable_oscillator.js'
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
- getOscillatorData(voice, this.workletDumpedSamplesList[voice.sample.sampleID], bufferOut);
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 getOscillatorData(voice, sampleData, outputBuffer)
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
  }