spessasynth_core 3.26.24 → 3.26.25
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/index.js +2 -1
- package/package.json +1 -1
- package/src/soundfont/basic_soundfont/basic_sample.js +106 -16
- package/src/soundfont/basic_soundfont/write_sf2/shdr.js +2 -1
- package/src/soundfont/basic_soundfont/write_sf2/write.js +1 -1
- package/src/soundfont/read_sf2/samples.js +25 -3
- package/src/synthetizer/audio_engine/engine_methods/controller_control/reset_controllers.js +10 -0
package/index.js
CHANGED
|
@@ -19,7 +19,7 @@ import { SynthesizerSnapshot } from "./src/synthetizer/audio_engine/snapshot/syn
|
|
|
19
19
|
import { ChannelSnapshot } from "./src/synthetizer/audio_engine/snapshot/channel_snapshot.js";
|
|
20
20
|
|
|
21
21
|
import { BasicSoundBank } from "./src/soundfont/basic_soundfont/basic_soundbank.js";
|
|
22
|
-
import { BasicSample } from "./src/soundfont/basic_soundfont/basic_sample.js";
|
|
22
|
+
import { BasicSample, sampleTypes } from "./src/soundfont/basic_soundfont/basic_sample.js";
|
|
23
23
|
import { BasicPresetZone } from "./src/soundfont/basic_soundfont/basic_preset_zone.js";
|
|
24
24
|
import { BasicInstrument } from "./src/soundfont/basic_soundfont/basic_instrument.js";
|
|
25
25
|
import { BasicPreset } from "./src/soundfont/basic_soundfont/basic_preset.js";
|
|
@@ -103,6 +103,7 @@ export {
|
|
|
103
103
|
generatorTypes,
|
|
104
104
|
DLSSources,
|
|
105
105
|
DLSDestinations,
|
|
106
|
+
sampleTypes,
|
|
106
107
|
|
|
107
108
|
|
|
108
109
|
// MIDI
|
package/package.json
CHANGED
|
@@ -8,6 +8,21 @@ import { SpessaSynthWarn } from "../../utils/loggin.js";
|
|
|
8
8
|
// should be reasonable for most cases
|
|
9
9
|
const RESAMPLE_RATE = 48000;
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* @enum {number}
|
|
13
|
+
*/
|
|
14
|
+
export const sampleTypes = {
|
|
15
|
+
monoSample: 1,
|
|
16
|
+
rightSample: 2,
|
|
17
|
+
leftSample: 4,
|
|
18
|
+
linkedSample: 8,
|
|
19
|
+
romMonoSample: 32769,
|
|
20
|
+
romRightSample: 32770,
|
|
21
|
+
romLeftSample: 32772,
|
|
22
|
+
romLinkedSample: 32776
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
|
|
11
26
|
/**
|
|
12
27
|
* @typedef {function} EncodeVorbisFunction
|
|
13
28
|
* @param channelAudioData {Float32Array[]}
|
|
@@ -45,14 +60,14 @@ export class BasicSample
|
|
|
45
60
|
samplePitchCorrection;
|
|
46
61
|
|
|
47
62
|
/**
|
|
48
|
-
*
|
|
49
|
-
* @type {
|
|
63
|
+
* Linked sample, unused if mono
|
|
64
|
+
* @type {BasicSample|undefined}
|
|
50
65
|
*/
|
|
51
|
-
|
|
66
|
+
linkedSample;
|
|
52
67
|
|
|
53
68
|
/**
|
|
54
|
-
*
|
|
55
|
-
* @type {number}
|
|
69
|
+
* The type of the sample, it can indicate an SF3 sample
|
|
70
|
+
* @type {sampleTypes|number}
|
|
56
71
|
*/
|
|
57
72
|
sampleType;
|
|
58
73
|
|
|
@@ -97,8 +112,7 @@ export class BasicSample
|
|
|
97
112
|
* @param sampleRate {number} The sample's rate in Hz
|
|
98
113
|
* @param samplePitch {number} The sample's pitch as a MIDI note number
|
|
99
114
|
* @param samplePitchCorrection {number} The sample's pitch correction in cents
|
|
100
|
-
* @param
|
|
101
|
-
* @param sampleType {number} The sample's type, an enum
|
|
115
|
+
* @param sampleType {sampleTypes|number} The sample's type, an enum that can indicate SF3
|
|
102
116
|
* @param loopStart {number} The sample's loop start relative to the sample start in sample points
|
|
103
117
|
* @param loopEnd {number} The sample's loop end relative to the sample start in sample points
|
|
104
118
|
*/
|
|
@@ -107,7 +121,6 @@ export class BasicSample
|
|
|
107
121
|
sampleRate,
|
|
108
122
|
samplePitch,
|
|
109
123
|
samplePitchCorrection,
|
|
110
|
-
sampleLink,
|
|
111
124
|
sampleType,
|
|
112
125
|
loopStart,
|
|
113
126
|
loopEnd
|
|
@@ -117,12 +130,21 @@ export class BasicSample
|
|
|
117
130
|
this.sampleRate = sampleRate;
|
|
118
131
|
this.samplePitch = samplePitch;
|
|
119
132
|
this.samplePitchCorrection = samplePitchCorrection;
|
|
120
|
-
this.sampleLink = sampleLink;
|
|
121
|
-
this.sampleType = sampleType;
|
|
122
133
|
this.sampleLoopStartIndex = loopStart;
|
|
123
134
|
this.sampleLoopEndIndex = loopEnd;
|
|
124
|
-
|
|
125
|
-
|
|
135
|
+
this.setSampleType(sampleType);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* If the sample is linked to another sample
|
|
140
|
+
* @returns {boolean}
|
|
141
|
+
*/
|
|
142
|
+
get isLinked()
|
|
143
|
+
{
|
|
144
|
+
return !this.isCompressed &&
|
|
145
|
+
(this.sampleType === sampleTypes.rightSample ||
|
|
146
|
+
this.sampleType === sampleTypes.leftSample ||
|
|
147
|
+
this.sampleType === sampleTypes.linkedSample);
|
|
126
148
|
}
|
|
127
149
|
|
|
128
150
|
/**
|
|
@@ -189,20 +211,88 @@ export class BasicSample
|
|
|
189
211
|
}
|
|
190
212
|
this.compressedData = encodeVorbis([audioData], 1, this.sampleRate, quality);
|
|
191
213
|
// flag as compressed
|
|
192
|
-
this.sampleType
|
|
193
|
-
this.isCompressed = true;
|
|
214
|
+
this.setSampleType(this.sampleType | 0x10);
|
|
194
215
|
}
|
|
195
216
|
catch (e)
|
|
196
217
|
{
|
|
197
218
|
SpessaSynthWarn(`Failed to compress ${this.sampleName}. Leaving as uncompressed!`);
|
|
198
|
-
this.isCompressed = false;
|
|
199
219
|
this.compressedData = undefined;
|
|
200
220
|
// flag as uncompressed
|
|
201
|
-
this.sampleType
|
|
221
|
+
this.setSampleType(this.sampleType & 0xEF);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* @param type {sampleTypes|number}
|
|
228
|
+
*/
|
|
229
|
+
setSampleType(type)
|
|
230
|
+
{
|
|
231
|
+
this.sampleType = type;
|
|
232
|
+
// https://github.com/FluidSynth/fluidsynth/wiki/SoundFont3Format
|
|
233
|
+
this.isCompressed = (type & 0x10) > 0;
|
|
234
|
+
if (!this.isLinked)
|
|
235
|
+
{
|
|
236
|
+
// unlink the other sample
|
|
237
|
+
if (this.linkedSample)
|
|
238
|
+
{
|
|
239
|
+
this.linkedSample.linkedSample = undefined;
|
|
240
|
+
this.linkedSample.sampleType = type;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
this.linkedSample = undefined;
|
|
244
|
+
}
|
|
245
|
+
if ((type & 0x8000) > 0 && this.linkedSample)
|
|
246
|
+
{
|
|
247
|
+
throw new Error("ROM samples cannot be linked.");
|
|
202
248
|
}
|
|
203
249
|
|
|
204
250
|
}
|
|
205
251
|
|
|
252
|
+
// noinspection JSUnusedGlobalSymbols
|
|
253
|
+
/**
|
|
254
|
+
* Unlinks a sample link
|
|
255
|
+
*/
|
|
256
|
+
unlinkSample()
|
|
257
|
+
{
|
|
258
|
+
this.setSampleType(sampleTypes.monoSample);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// noinspection JSUnusedGlobalSymbols
|
|
262
|
+
/**
|
|
263
|
+
* Links a stereo sample
|
|
264
|
+
* @param sample {BasicSample}
|
|
265
|
+
* @param type {sampleTypes}
|
|
266
|
+
*/
|
|
267
|
+
setLinkedSample(sample, type)
|
|
268
|
+
{
|
|
269
|
+
if (this.isCompressed)
|
|
270
|
+
{
|
|
271
|
+
throw new Error("Cannot link a compressed sample.");
|
|
272
|
+
}
|
|
273
|
+
this.linkedSample = sample;
|
|
274
|
+
sample.linkedSample = this;
|
|
275
|
+
if (type === sampleTypes.leftSample)
|
|
276
|
+
{
|
|
277
|
+
this.setSampleType(sampleTypes.leftSample);
|
|
278
|
+
sample.setSampleType(sampleTypes.rightSample);
|
|
279
|
+
}
|
|
280
|
+
else if (type === sampleTypes.rightSample)
|
|
281
|
+
{
|
|
282
|
+
this.setSampleType(sampleTypes.rightSample);
|
|
283
|
+
sample.setSampleType(sampleTypes.leftSample);
|
|
284
|
+
}
|
|
285
|
+
else if (type === sampleTypes.linkedSample)
|
|
286
|
+
{
|
|
287
|
+
this.setSampleType(sampleTypes.linkedSample);
|
|
288
|
+
sample.setSampleType(sampleTypes.linkedSample);
|
|
289
|
+
}
|
|
290
|
+
else
|
|
291
|
+
{
|
|
292
|
+
throw new Error("Invalid sample type: " + type);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
206
296
|
/**
|
|
207
297
|
* @param instrument {BasicInstrument}
|
|
208
298
|
*/
|
|
@@ -40,7 +40,8 @@ export function getSHDR(smplStartOffsets, smplEndOffsets)
|
|
|
40
40
|
shdrData[shdrData.currentIndex++] = sample.samplePitch;
|
|
41
41
|
shdrData[shdrData.currentIndex++] = sample.samplePitchCorrection;
|
|
42
42
|
// sample link
|
|
43
|
-
|
|
43
|
+
const sampleLinkIndex = this.samples.indexOf(sample.linkedSample);
|
|
44
|
+
writeWord(shdrData, Math.max(0, sampleLinkIndex));
|
|
44
45
|
// sample type: write raw because we simply copy compressed samples
|
|
45
46
|
writeWord(shdrData, sample.sampleType);
|
|
46
47
|
});
|
|
@@ -8,6 +8,12 @@ import { BasicSample } from "../basic_soundfont/basic_sample.js";
|
|
|
8
8
|
|
|
9
9
|
export class SoundFontSample extends BasicSample
|
|
10
10
|
{
|
|
11
|
+
/**
|
|
12
|
+
* Linked sample index for retrieving linked samples in sf2
|
|
13
|
+
* @type {number}
|
|
14
|
+
*/
|
|
15
|
+
linkedSampleIndex;
|
|
16
|
+
|
|
11
17
|
/**
|
|
12
18
|
* Creates a sample
|
|
13
19
|
* @param sampleName {string}
|
|
@@ -18,7 +24,7 @@ export class SoundFontSample extends BasicSample
|
|
|
18
24
|
* @param sampleRate {number}
|
|
19
25
|
* @param samplePitch {number}
|
|
20
26
|
* @param samplePitchCorrection {number}
|
|
21
|
-
* @param
|
|
27
|
+
* @param linkedSampleIndex {number}
|
|
22
28
|
* @param sampleType {number}
|
|
23
29
|
* @param smplArr {IndexedByteArray|Float32Array}
|
|
24
30
|
* @param sampleIndex {number} initial sample index when loading the sfont
|
|
@@ -34,7 +40,7 @@ export class SoundFontSample extends BasicSample
|
|
|
34
40
|
sampleRate,
|
|
35
41
|
samplePitch,
|
|
36
42
|
samplePitchCorrection,
|
|
37
|
-
|
|
43
|
+
linkedSampleIndex,
|
|
38
44
|
sampleType,
|
|
39
45
|
smplArr,
|
|
40
46
|
sampleIndex,
|
|
@@ -46,7 +52,6 @@ export class SoundFontSample extends BasicSample
|
|
|
46
52
|
sampleRate,
|
|
47
53
|
samplePitch,
|
|
48
54
|
samplePitchCorrection,
|
|
49
|
-
sampleLink,
|
|
50
55
|
sampleType,
|
|
51
56
|
sampleLoopStartIndex - (sampleStartIndex / 2),
|
|
52
57
|
sampleLoopEndIndex - (sampleStartIndex / 2)
|
|
@@ -69,6 +74,19 @@ export class SoundFontSample extends BasicSample
|
|
|
69
74
|
this.sampleLength = 99999999; // set to 999,999 before we decode it
|
|
70
75
|
}
|
|
71
76
|
this.isDataRaw = isDataRaw;
|
|
77
|
+
this.linkedSampleIndex = linkedSampleIndex;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @param samplesArray {BasicSample[]}
|
|
82
|
+
*/
|
|
83
|
+
getLinkedSample(samplesArray)
|
|
84
|
+
{
|
|
85
|
+
if (this.isCompressed || this.linkedSample || !this.isLinked)
|
|
86
|
+
{
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
this.setLinkedSample(samplesArray[this.linkedSampleIndex], this.sampleType);
|
|
72
90
|
}
|
|
73
91
|
|
|
74
92
|
/**
|
|
@@ -247,6 +265,10 @@ export function readSamples(sampleHeadersChunk, smplChunkData, isSmplDataRaw = t
|
|
|
247
265
|
}
|
|
248
266
|
// remove EOS
|
|
249
267
|
samples.pop();
|
|
268
|
+
|
|
269
|
+
// link samples
|
|
270
|
+
samples.forEach(s => s.getLinkedSample(samples));
|
|
271
|
+
|
|
250
272
|
return samples;
|
|
251
273
|
}
|
|
252
274
|
|
|
@@ -102,6 +102,16 @@ export function resetAllControllers(log = true)
|
|
|
102
102
|
LSB: lsb
|
|
103
103
|
});
|
|
104
104
|
}
|
|
105
|
+
|
|
106
|
+
// restore channel pressure
|
|
107
|
+
if (this.midiAudioChannels[channelNumber].lockedControllers[NON_CC_INDEX_OFFSET + modulatorSources.channelPressure] === false)
|
|
108
|
+
{
|
|
109
|
+
const val = this.midiAudioChannels[channelNumber].midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.channelPressure] >> 7;
|
|
110
|
+
this.callEvent("channelpressure", {
|
|
111
|
+
channel: channelNumber,
|
|
112
|
+
pressure: val
|
|
113
|
+
});
|
|
114
|
+
}
|
|
105
115
|
}
|
|
106
116
|
this.tunings = [];
|
|
107
117
|
this.tunings = [];
|