spessasynth_lib 3.25.4 → 3.25.6
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/README.md +1 -1
- package/midi_parser/basic_midi.js +0 -28
- package/package.json +1 -1
- package/soundfont/basic_soundfont/basic_instrument.js +18 -14
- package/soundfont/basic_soundfont/basic_preset.js +64 -59
- package/soundfont/basic_soundfont/basic_sample.js +75 -55
- package/soundfont/basic_soundfont/basic_soundfont.js +121 -68
- package/soundfont/dls/dls_preset.js +3 -3
- package/soundfont/dls/dls_soundfont.js +1 -1
- package/soundfont/dls/dls_sources.js +3 -2
- package/soundfont/dls/read_instrument.js +1 -1
- package/soundfont/read_sf2/presets.js +6 -6
- package/soundfont/read_sf2/samples.js +19 -18
- package/soundfont/read_sf2/soundfont.js +2 -2
- package/soundfont/read_sf2/zones.js +2 -3
- package/synthetizer/synthetizer.js +21 -0
- package/synthetizer/worklet_processor.min.js +11 -11
- package/synthetizer/worklet_system/worklet_methods/controller_control/master_parameters.js +3 -1
- package/synthetizer/worklet_system/worklet_methods/program_change.js +1 -0
- package/synthetizer/worklet_system/worklet_methods/soundfont_management/reload_sound_font.js +0 -2
- package/synthetizer/worklet_system/worklet_methods/worklet_soundfont_manager/worklet_soundfont_manager.js +0 -13
- package/synthetizer/worklet_system/worklet_utilities/lowpass_filter.js +16 -10
package/README.md
CHANGED
|
@@ -55,7 +55,7 @@ Supported formats list:
|
|
|
55
55
|
- **Easy to Use:** *Basic setup is just [two lines of code!](https://github.com/spessasus/SpessaSynth/wiki/Usage-As-Library#minimal-setup)*
|
|
56
56
|
- **No dependencies:** *Batteries included!*
|
|
57
57
|
|
|
58
|
-
### Powerful Synthesizer
|
|
58
|
+
### Powerful MIDI Synthesizer
|
|
59
59
|
- Suitable for both **real-time** and **offline** synthesis
|
|
60
60
|
- **Excellent SoundFont support:**
|
|
61
61
|
- **Full Generator Support**
|
|
@@ -533,35 +533,7 @@ class BasicMIDI extends MIDISequenceData
|
|
|
533
533
|
);
|
|
534
534
|
}
|
|
535
535
|
|
|
536
|
-
// lyrics fix:
|
|
537
|
-
// sometimes, all lyrics events lack spaces at the start or end of the lyric
|
|
538
|
-
// then, and only then, add space at the end of each lyric
|
|
539
|
-
// space ASCII is 32
|
|
540
|
-
let lacksSpaces = true;
|
|
541
|
-
for (const lyric of this.lyrics)
|
|
542
|
-
{
|
|
543
|
-
if (lyric[0] === 32 || lyric[lyric.length - 1] === 32)
|
|
544
|
-
{
|
|
545
|
-
lacksSpaces = false;
|
|
546
|
-
break;
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
536
|
|
|
550
|
-
if (lacksSpaces)
|
|
551
|
-
{
|
|
552
|
-
this.lyrics = this.lyrics.map(lyric =>
|
|
553
|
-
{
|
|
554
|
-
// One exception: hyphens at the end. Don't add a space to them
|
|
555
|
-
if (lyric[lyric.length - 1] === 45)
|
|
556
|
-
{
|
|
557
|
-
return lyric;
|
|
558
|
-
}
|
|
559
|
-
const withSpaces = new Uint8Array(lyric.length + 1);
|
|
560
|
-
withSpaces.set(lyric, 0);
|
|
561
|
-
withSpaces[lyric.length] = 32;
|
|
562
|
-
return withSpaces;
|
|
563
|
-
});
|
|
564
|
-
}
|
|
565
537
|
/**
|
|
566
538
|
* The total playback time, in seconds
|
|
567
539
|
* @type {number}
|
package/package.json
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
export class BasicInstrument
|
|
2
2
|
{
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
3
|
+
/**
|
|
4
|
+
* The instrument's name
|
|
5
|
+
* @type {string}
|
|
6
|
+
*/
|
|
7
|
+
instrumentName = "";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The instrument's zones
|
|
11
|
+
* @type {BasicInstrumentZone[]}
|
|
12
|
+
*/
|
|
13
|
+
instrumentZones = [];
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Instrument's use count, used for trimming
|
|
17
|
+
* @type {number}
|
|
18
|
+
* @private
|
|
19
|
+
*/
|
|
20
|
+
_useCount = 0;
|
|
17
21
|
|
|
18
22
|
/**
|
|
19
23
|
* @returns {number}
|
|
@@ -14,69 +14,69 @@ import { isXGDrums } from "../../utils/xg_hacks.js";
|
|
|
14
14
|
export class BasicPreset
|
|
15
15
|
{
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
17
|
+
* The parent soundbank instance
|
|
18
|
+
* Currently used for determining default modulators and XG status
|
|
19
|
+
* @type {BasicSoundBank}
|
|
18
20
|
*/
|
|
19
|
-
|
|
21
|
+
parentSoundBank;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The preset's name
|
|
25
|
+
* @type {string}
|
|
26
|
+
*/
|
|
27
|
+
presetName = "";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The preset's MIDI program number
|
|
31
|
+
* @type {number}
|
|
32
|
+
*/
|
|
33
|
+
program = 0;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The preset's MIDI bank number
|
|
37
|
+
* @type {number}
|
|
38
|
+
*/
|
|
39
|
+
bank = 0;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The preset's zones
|
|
43
|
+
* @type {BasicPresetZone[]}
|
|
44
|
+
*/
|
|
45
|
+
presetZones = [];
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Stores already found getSamplesAndGenerators for reuse
|
|
49
|
+
* @type {SampleAndGenerators[][][]}
|
|
50
|
+
*/
|
|
51
|
+
foundSamplesAndGenerators = [];
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* unused metadata
|
|
55
|
+
* @type {number}
|
|
56
|
+
*/
|
|
57
|
+
library = 0;
|
|
58
|
+
/**
|
|
59
|
+
* unused metadata
|
|
60
|
+
* @type {number}
|
|
61
|
+
*/
|
|
62
|
+
genre = 0;
|
|
63
|
+
/**
|
|
64
|
+
* unused metadata
|
|
65
|
+
* @type {number}
|
|
66
|
+
*/
|
|
67
|
+
morphology = 0;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Creates a new preset representation
|
|
71
|
+
* @param parentSoundBank {BasicSoundBank}
|
|
72
|
+
*/
|
|
73
|
+
constructor(parentSoundBank)
|
|
20
74
|
{
|
|
21
|
-
|
|
22
|
-
* The preset's name
|
|
23
|
-
* @type {string}
|
|
24
|
-
*/
|
|
25
|
-
this.presetName = "";
|
|
26
|
-
/**
|
|
27
|
-
* The preset's MIDI program number
|
|
28
|
-
* @type {number}
|
|
29
|
-
*/
|
|
30
|
-
this.program = 0;
|
|
31
|
-
/**
|
|
32
|
-
* The preset's MIDI bank number
|
|
33
|
-
* @type {number}
|
|
34
|
-
*/
|
|
35
|
-
this.bank = 0;
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* The preset's zones
|
|
39
|
-
* @type {BasicPresetZone[]}
|
|
40
|
-
*/
|
|
41
|
-
this.presetZones = [];
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* SampleID offset for this preset
|
|
45
|
-
* @type {number}
|
|
46
|
-
*/
|
|
47
|
-
this.sampleIDOffset = 0;
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Stores already found getSamplesAndGenerators for reuse
|
|
51
|
-
* @type {SampleAndGenerators[][][]}
|
|
52
|
-
*/
|
|
53
|
-
this.foundSamplesAndGenerators = [];
|
|
75
|
+
this.parentSoundBank = parentSoundBank;
|
|
54
76
|
for (let i = 0; i < 128; i++)
|
|
55
77
|
{
|
|
56
78
|
this.foundSamplesAndGenerators[i] = [];
|
|
57
79
|
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* unused metadata
|
|
61
|
-
* @type {number}
|
|
62
|
-
*/
|
|
63
|
-
this.library = 0;
|
|
64
|
-
/**
|
|
65
|
-
* unused metadata
|
|
66
|
-
* @type {number}
|
|
67
|
-
*/
|
|
68
|
-
this.genre = 0;
|
|
69
|
-
/**
|
|
70
|
-
* unused metadata
|
|
71
|
-
* @type {number}
|
|
72
|
-
*/
|
|
73
|
-
this.morphology = 0;
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Default modulators
|
|
77
|
-
* @type {Modulator[]}
|
|
78
|
-
*/
|
|
79
|
-
this.defaultModulators = modulators;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
/**
|
|
@@ -86,8 +86,13 @@ export class BasicPreset
|
|
|
86
86
|
*/
|
|
87
87
|
isDrumPreset(allowXG, allowSFX = false)
|
|
88
88
|
{
|
|
89
|
+
const xg = allowXG && this.parentSoundBank.isXGBank;
|
|
90
|
+
console.log(xg);
|
|
89
91
|
// sfx is not cool
|
|
90
|
-
return this.bank === 128 || (
|
|
92
|
+
return this.bank === 128 || (
|
|
93
|
+
xg &&
|
|
94
|
+
(isXGDrums(this.bank) && (this.bank !== 126 || allowSFX))
|
|
95
|
+
);
|
|
91
96
|
}
|
|
92
97
|
|
|
93
98
|
deletePreset()
|
|
@@ -286,7 +291,7 @@ export class BasicPreset
|
|
|
286
291
|
// default mods
|
|
287
292
|
addUniqueMods(
|
|
288
293
|
instrumentModulators,
|
|
289
|
-
this.defaultModulators
|
|
294
|
+
this.parentSoundBank.defaultModulators
|
|
290
295
|
);
|
|
291
296
|
|
|
292
297
|
/**
|
|
@@ -10,6 +10,79 @@ const RESAMPLE_RATE = 48000;
|
|
|
10
10
|
|
|
11
11
|
export class BasicSample
|
|
12
12
|
{
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The sample's name
|
|
16
|
+
* @type {string}
|
|
17
|
+
*/
|
|
18
|
+
sampleName;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Sample rate in Hz
|
|
22
|
+
* @type {number}
|
|
23
|
+
*/
|
|
24
|
+
sampleRate;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Original pitch of the sample as a MIDI note number
|
|
28
|
+
* @type {number}
|
|
29
|
+
*/
|
|
30
|
+
samplePitch;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Pitch correction, in cents. Can be negative
|
|
34
|
+
* @type {number}
|
|
35
|
+
*/
|
|
36
|
+
samplePitchCorrection;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Sample link, currently unused here
|
|
40
|
+
* @type {number}
|
|
41
|
+
*/
|
|
42
|
+
sampleLink;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Type of the sample, currently only used for SF3
|
|
46
|
+
* @type {number}
|
|
47
|
+
*/
|
|
48
|
+
sampleType;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Relative to the start of the sample in sample points
|
|
52
|
+
* @type {number}
|
|
53
|
+
*/
|
|
54
|
+
sampleLoopStartIndex;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Relative to the start of the sample in sample points
|
|
58
|
+
* @type {number}
|
|
59
|
+
*/
|
|
60
|
+
sampleLoopEndIndex;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Indicates if the sample is compressed
|
|
64
|
+
* @type {boolean}
|
|
65
|
+
*/
|
|
66
|
+
isCompressed;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The compressed sample data if it was compressed by spessasynth
|
|
70
|
+
* @type {Uint8Array}
|
|
71
|
+
*/
|
|
72
|
+
compressedData = undefined;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The sample's use count
|
|
76
|
+
* @type {number}
|
|
77
|
+
*/
|
|
78
|
+
useCount = 0;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The sample's audio data
|
|
82
|
+
* @type {Float32Array}
|
|
83
|
+
*/
|
|
84
|
+
sampleData = undefined;
|
|
85
|
+
|
|
13
86
|
/**
|
|
14
87
|
* The basic representation of a soundfont sample
|
|
15
88
|
* @param sampleName {string} The sample's name
|
|
@@ -32,72 +105,19 @@ export class BasicSample
|
|
|
32
105
|
loopEnd
|
|
33
106
|
)
|
|
34
107
|
{
|
|
35
|
-
/**
|
|
36
|
-
* Sample's name
|
|
37
|
-
* @type {string}
|
|
38
|
-
*/
|
|
39
108
|
this.sampleName = sampleName;
|
|
40
|
-
/**
|
|
41
|
-
* Sample rate in Hz
|
|
42
|
-
* @type {number}
|
|
43
|
-
*/
|
|
44
109
|
this.sampleRate = sampleRate;
|
|
45
|
-
/**
|
|
46
|
-
* Original pitch of the sample as a MIDI note number
|
|
47
|
-
* @type {number}
|
|
48
|
-
*/
|
|
49
110
|
this.samplePitch = samplePitch;
|
|
50
|
-
/**
|
|
51
|
-
* Pitch correction, in cents. Can be negative
|
|
52
|
-
* @type {number}
|
|
53
|
-
*/
|
|
54
111
|
this.samplePitchCorrection = samplePitchCorrection;
|
|
55
|
-
/**
|
|
56
|
-
* Sample link, currently unused.
|
|
57
|
-
* @type {number}
|
|
58
|
-
*/
|
|
59
112
|
this.sampleLink = sampleLink;
|
|
60
|
-
/**
|
|
61
|
-
* Type of the sample, an enum
|
|
62
|
-
* @type {number}
|
|
63
|
-
*/
|
|
64
113
|
this.sampleType = sampleType;
|
|
65
|
-
/**
|
|
66
|
-
* Relative to the start of the sample in sample points
|
|
67
|
-
* @type {number}
|
|
68
|
-
*/
|
|
69
114
|
this.sampleLoopStartIndex = loopStart;
|
|
70
|
-
/**
|
|
71
|
-
* Relative to the start of the sample in sample points
|
|
72
|
-
* @type {number}
|
|
73
|
-
*/
|
|
74
115
|
this.sampleLoopEndIndex = loopEnd;
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Indicates if the sample is compressed
|
|
78
|
-
* @type {boolean}
|
|
79
|
-
*/
|
|
116
|
+
// https://github.com/FluidSynth/fluidsynth/wiki/SoundFont3Format
|
|
80
117
|
this.isCompressed = (sampleType & 0x10) > 0;
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* The compressed sample data if it was compressed by spessasynth
|
|
84
|
-
* @type {Uint8Array}
|
|
85
|
-
*/
|
|
86
|
-
this.compressedData = undefined;
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* The sample's use count
|
|
90
|
-
* @type {number}
|
|
91
|
-
*/
|
|
92
|
-
this.useCount = 0;
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* The sample's audio data
|
|
96
|
-
* @type {Float32Array}
|
|
97
|
-
*/
|
|
98
|
-
this.sampleData = undefined;
|
|
99
118
|
}
|
|
100
119
|
|
|
120
|
+
|
|
101
121
|
/**
|
|
102
122
|
* @returns {Uint8Array|IndexedByteArray}
|
|
103
123
|
*/
|
|
@@ -18,42 +18,49 @@ import { isXGDrums } from "../../utils/xg_hacks.js";
|
|
|
18
18
|
|
|
19
19
|
class BasicSoundBank
|
|
20
20
|
{
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Soundfont's info stored as name: value. ifil and iver are stored as string representation of float (e.g., 2.1)
|
|
24
|
+
* @type {Object<string, string|IndexedByteArray>}
|
|
25
|
+
*/
|
|
26
|
+
soundFontInfo = {};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The soundfont's presets
|
|
30
|
+
* @type {BasicPreset[]}
|
|
31
|
+
*/
|
|
32
|
+
presets = [];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The soundfont's samples
|
|
36
|
+
* @type {BasicSample[]}
|
|
37
|
+
*/
|
|
38
|
+
samples = [];
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The soundfont's instruments
|
|
42
|
+
* @type {BasicInstrument[]}
|
|
43
|
+
*/
|
|
44
|
+
instruments = [];
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Soundfont's default modulatorss
|
|
48
|
+
* @type {Modulator[]}
|
|
49
|
+
*/
|
|
50
|
+
defaultModulators = defaultModulators.map(m => Modulator.copy(m));
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Checks for XG drumsets and considers if this soundfont is XG.
|
|
54
|
+
* @type {boolean}
|
|
55
|
+
*/
|
|
56
|
+
isXGBank = false;
|
|
57
|
+
|
|
21
58
|
/**
|
|
22
59
|
* Creates a new basic soundfont template
|
|
23
60
|
* @param data {undefined|{presets: BasicPreset[], info: Object<string, string>}}
|
|
24
61
|
*/
|
|
25
62
|
constructor(data = undefined)
|
|
26
63
|
{
|
|
27
|
-
/**
|
|
28
|
-
* Soundfont's info stored as name: value. ifil and iver are stored as string representation of float (e.g., 2.1)
|
|
29
|
-
* @type {Object<string, string|IndexedByteArray>}
|
|
30
|
-
*/
|
|
31
|
-
this.soundFontInfo = {};
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* The soundfont's presets
|
|
35
|
-
* @type {BasicPreset[]}
|
|
36
|
-
*/
|
|
37
|
-
this.presets = [];
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* The soundfont's samples
|
|
41
|
-
* @type {BasicSample[]}
|
|
42
|
-
*/
|
|
43
|
-
this.samples = [];
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* The soundfont's instruments
|
|
47
|
-
* @type {BasicInstrument[]}
|
|
48
|
-
*/
|
|
49
|
-
this.instruments = [];
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Soundfont's default modulatorss
|
|
53
|
-
* @type {Modulator[]}
|
|
54
|
-
*/
|
|
55
|
-
this.defaultModulators = defaultModulators.map(m => Modulator.copy(m));
|
|
56
|
-
|
|
57
64
|
if (data?.presets)
|
|
58
65
|
{
|
|
59
66
|
this.presets.push(...data.presets);
|
|
@@ -135,7 +142,7 @@ class BasicSoundBank
|
|
|
135
142
|
const pZone = new BasicPresetZone();
|
|
136
143
|
pZone.instrument = inst;
|
|
137
144
|
|
|
138
|
-
const preset = new BasicPreset(font
|
|
145
|
+
const preset = new BasicPreset(font);
|
|
139
146
|
preset.presetName = "Saw Wave";
|
|
140
147
|
preset.presetZones.push(pZone);
|
|
141
148
|
font.presets.push(preset);
|
|
@@ -143,9 +150,45 @@ class BasicSoundBank
|
|
|
143
150
|
font.soundFontInfo["ifil"] = "2.1";
|
|
144
151
|
font.soundFontInfo["isng"] = "EMU8000";
|
|
145
152
|
font.soundFontInfo["INAM"] = "Dummy";
|
|
153
|
+
font._parseInternal();
|
|
146
154
|
return font.write().buffer;
|
|
147
155
|
}
|
|
148
156
|
|
|
157
|
+
/**
|
|
158
|
+
* parses the bank after loading is done
|
|
159
|
+
* @protected
|
|
160
|
+
*/
|
|
161
|
+
_parseInternal()
|
|
162
|
+
{
|
|
163
|
+
this.isXGBank = false;
|
|
164
|
+
// definitions for XG:
|
|
165
|
+
// at least one preset with bank 127, 126 or 120
|
|
166
|
+
// MUST be a valid XG bank.
|
|
167
|
+
// allowed banks: (see XG specification)
|
|
168
|
+
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 24,
|
|
169
|
+
// 25, 27, 28, 29, 30, 31, 32, 33, 40, 41, 48,
|
|
170
|
+
// 64, 65, 66, 126, 127
|
|
171
|
+
const allowedPrograms = new Set([
|
|
172
|
+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 24,
|
|
173
|
+
25, 27, 28, 29, 30, 31, 32, 33, 40, 41, 48,
|
|
174
|
+
64, 65, 66, 126, 127
|
|
175
|
+
]);
|
|
176
|
+
for (const preset of this.presets)
|
|
177
|
+
{
|
|
178
|
+
if (isXGDrums(preset.bank))
|
|
179
|
+
{
|
|
180
|
+
this.isXGBank = true;
|
|
181
|
+
if (!allowedPrograms.has(preset.program))
|
|
182
|
+
{
|
|
183
|
+
// not valid!
|
|
184
|
+
console.log("not XG!!");
|
|
185
|
+
this.isXGBank = false;
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
149
192
|
/**
|
|
150
193
|
* Trims a sound bank to only contain samples in a given MIDI file
|
|
151
194
|
* @param mid {BasicMIDI} - the MIDI file
|
|
@@ -381,16 +424,7 @@ class BasicSoundBank
|
|
|
381
424
|
}
|
|
382
425
|
|
|
383
426
|
/**
|
|
384
|
-
*
|
|
385
|
-
* @param offset {number}
|
|
386
|
-
*/
|
|
387
|
-
setSampleIDOffset(offset)
|
|
388
|
-
{
|
|
389
|
-
this.presets.forEach(p => p.sampleIDOffset = offset);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
/**
|
|
393
|
-
* Get the appropriate preset, undefined if not foun d
|
|
427
|
+
* Get the appropriate preset, undefined if not found
|
|
394
428
|
* @param bankNr {number}
|
|
395
429
|
* @param programNr {number}
|
|
396
430
|
* @param allowXGDrums {boolean} if true, allows XG drum banks (120, 126 and 127) as drum preset
|
|
@@ -398,14 +432,22 @@ class BasicSoundBank
|
|
|
398
432
|
*/
|
|
399
433
|
getPresetNoFallback(bankNr, programNr, allowXGDrums = false)
|
|
400
434
|
{
|
|
435
|
+
const isDrum = bankNr === 128 || (allowXGDrums && isXGDrums(bankNr));
|
|
401
436
|
// check for exact match
|
|
402
|
-
|
|
437
|
+
let p;
|
|
438
|
+
if (isDrum)
|
|
439
|
+
{
|
|
440
|
+
p = this.presets.find(p => p.bank === bankNr && p.isDrumPreset(allowXGDrums) && p.program === programNr);
|
|
441
|
+
}
|
|
442
|
+
else
|
|
443
|
+
{
|
|
444
|
+
p = this.presets.find(p => p.bank === bankNr && p.program === programNr);
|
|
445
|
+
}
|
|
403
446
|
if (p)
|
|
404
447
|
{
|
|
405
448
|
return p;
|
|
406
449
|
}
|
|
407
450
|
// no match...
|
|
408
|
-
const isDrum = bankNr === 128 || (allowXGDrums && isXGDrums(bankNr));
|
|
409
451
|
if (isDrum)
|
|
410
452
|
{
|
|
411
453
|
if (allowXGDrums)
|
|
@@ -430,36 +472,47 @@ class BasicSoundBank
|
|
|
430
472
|
*/
|
|
431
473
|
getPreset(bankNr, programNr, allowXGDrums = false)
|
|
432
474
|
{
|
|
433
|
-
// check for exact match
|
|
434
|
-
let preset = this.presets.find(p => p.bank === bankNr && p.program === programNr);
|
|
435
475
|
const isDrums = bankNr === 128 || (allowXGDrums && isXGDrums(bankNr));
|
|
436
|
-
|
|
476
|
+
// check for exact match
|
|
477
|
+
let preset;
|
|
478
|
+
// only allow drums if the preset is considered to be a drum preset
|
|
479
|
+
if (isDrums)
|
|
437
480
|
{
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
if (preset)
|
|
481
|
+
preset = this.presets.find(p => p.bank === bankNr && p.isDrumPreset(allowXGDrums) && p.program === programNr);
|
|
482
|
+
}
|
|
483
|
+
else
|
|
484
|
+
{
|
|
485
|
+
preset = this.presets.find(p => p.bank === bankNr && p.program === programNr);
|
|
486
|
+
}
|
|
487
|
+
if (preset)
|
|
488
|
+
{
|
|
489
|
+
return preset;
|
|
490
|
+
}
|
|
491
|
+
// no match...
|
|
492
|
+
if (isDrums)
|
|
493
|
+
{
|
|
494
|
+
// drum preset: find any preset with bank 128
|
|
495
|
+
preset = this.presets.find(p => p.isDrumPreset(allowXGDrums) && p.program === programNr);
|
|
496
|
+
if (!preset)
|
|
455
497
|
{
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
consoleColors.warn,
|
|
459
|
-
consoleColors.recognized
|
|
460
|
-
);
|
|
498
|
+
// only allow 128, otherwise it would default to XG SFX
|
|
499
|
+
preset = this.presets.find(p => p.isDrumPreset(allowXGDrums));
|
|
461
500
|
}
|
|
462
501
|
}
|
|
502
|
+
else
|
|
503
|
+
{
|
|
504
|
+
// non-drum preset: find any preset with the given program that is not a drum preset
|
|
505
|
+
preset = this.presets.find(p => p.program === programNr && !p.isDrumPreset(allowXGDrums));
|
|
506
|
+
}
|
|
507
|
+
if (preset)
|
|
508
|
+
{
|
|
509
|
+
SpessaSynthWarn(
|
|
510
|
+
`%cPreset ${bankNr}.${programNr} not found. Replaced with %c${preset.presetName} (${preset.bank}.${preset.program})`,
|
|
511
|
+
consoleColors.warn,
|
|
512
|
+
consoleColors.recognized
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
|
|
463
516
|
// no preset, use the first one available
|
|
464
517
|
if (!preset)
|
|
465
518
|
{
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { BasicPreset } from "../basic_soundfont/basic_preset.js";
|
|
2
2
|
import { BasicPresetZone } from "../basic_soundfont/basic_zones.js";
|
|
3
3
|
import { BasicInstrument } from "../basic_soundfont/basic_instrument.js";
|
|
4
|
-
import { defaultModulators } from "../basic_soundfont/modulator.js";
|
|
5
4
|
|
|
6
5
|
export class DLSPreset extends BasicPreset
|
|
7
6
|
{
|
|
8
7
|
/**
|
|
9
8
|
* Creates a new DLS preset
|
|
9
|
+
* @param dls {BasicSoundBank}
|
|
10
10
|
* @param ulBank {number}
|
|
11
11
|
* @param ulInstrument {number}
|
|
12
12
|
*/
|
|
13
|
-
constructor(ulBank, ulInstrument)
|
|
13
|
+
constructor(dls, ulBank, ulInstrument)
|
|
14
14
|
{
|
|
15
15
|
// use stock default modulators, dls won't ever have DMOD chunk
|
|
16
|
-
super(
|
|
16
|
+
super(dls);
|
|
17
17
|
this.program = ulInstrument & 127;
|
|
18
18
|
const bankMSB = (ulBank >> 8) & 127;
|
|
19
19
|
const bankLSB = ulBank & 127;
|
|
@@ -115,7 +115,7 @@ class DLSSoundFont extends BasicSoundBank
|
|
|
115
115
|
|
|
116
116
|
// sort presets
|
|
117
117
|
this.presets.sort((a, b) => (a.program - b.program) + (a.bank - b.bank));
|
|
118
|
-
|
|
118
|
+
this._parseInternal();
|
|
119
119
|
SpessaSynthInfo(
|
|
120
120
|
`%cParsing finished! %c"${this.soundFontInfo["INAM"] || "UNNAMED"}"%c has %c${this.presets.length} %cpresets,
|
|
121
121
|
%c${this.instruments.length}%c instruments and %c${this.samples.length}%c samples.`,
|
|
@@ -20,8 +20,9 @@ export const DLSSources = {
|
|
|
20
20
|
volume: 0x87,
|
|
21
21
|
pan: 0x8a,
|
|
22
22
|
expression: 0x8b,
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
// note: these are flipped unintentionally in DLS2 table 9. Argh!
|
|
24
|
+
chorus: 0xdd,
|
|
25
|
+
reverb: 0xdb,
|
|
25
26
|
|
|
26
27
|
pitchWheelRange: 0x100,
|
|
27
28
|
fineTune: 0x101,
|