spessasynth_lib 3.24.26 → 3.24.28
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 +28 -10
- package/index.js +20 -0
- package/midi_parser/basic_midi.js +36 -17
- package/package.json +2 -1
- package/sequencer/sequencer.js +2 -2
- package/sequencer/worklet_sequencer/process_event.js +4 -1
- package/sequencer/worklet_sequencer/sequencer_message.js +1 -1
- package/soundfont/basic_soundfont/modulator.js +151 -119
- package/soundfont/dls/articulator_converter.js +7 -7
- package/soundfont/dls/dls_sources.js +28 -28
- package/soundfont/read_sf2/modulators.js +6 -7
- package/synthetizer/worklet_processor.min.js +15 -15
- package/synthetizer/worklet_system/worklet_methods/controller_control/reset_controllers.js +17 -3
- package/synthetizer/worklet_system/worklet_utilities/controller_tables.js +2 -1
- package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +1 -1
package/README.md
CHANGED
|
@@ -32,13 +32,28 @@ document.getElementById("button").onclick = async () =>
|
|
|
32
32
|
|
|
33
33
|
## Current Features
|
|
34
34
|
|
|
35
|
+
### Numerous Format Support
|
|
36
|
+
Supported formats list:
|
|
37
|
+
- `.mid` - Standard MIDI File
|
|
38
|
+
- `.kar` - Soft Karaoke MIDI File
|
|
39
|
+
- `.sf2` - SoundFont2 File
|
|
40
|
+
- `.sf3` - SoundFont2 Compressed File
|
|
41
|
+
- `.sfogg` - SF2Pack With Vorbis Compression
|
|
42
|
+
- `.dls` - Downloadable Sounds Levels 1 & 2 (as well as Mobile DLS)
|
|
43
|
+
- `.rmi` - RIFF MIDI File
|
|
44
|
+
- `.rmi` - RIFF MIDI File With Embedded DLS
|
|
45
|
+
- `.rmi` - [RIFF MIDI File With Embedded SF2](https://github.com/spessasus/sf2-rmidi-specification)
|
|
46
|
+
|
|
47
|
+
*With [an easy way of converting between them!](https://github.com/spessasus/SpessaSynth/wiki/Converting-Between-Formats)*
|
|
48
|
+
|
|
35
49
|
### Easy Integration
|
|
36
|
-
- **Modular design:** Easy integration into other projects (load what you need)
|
|
37
|
-
- **[Detailed documentation:](https://github.com/spessasus/SpessaSynth/wiki/Home)** With [examples!](https://github.com/spessasus/SpessaSynth/wiki/Usage-As-Library#examples)
|
|
38
|
-
- **
|
|
39
|
-
- **
|
|
50
|
+
- **Modular design:** *Easy integration into other projects (load what you need)*
|
|
51
|
+
- **[Detailed documentation:](https://github.com/spessasus/SpessaSynth/wiki/Home)** *With [examples!](https://github.com/spessasus/SpessaSynth/wiki/Usage-As-Library#examples)*
|
|
52
|
+
- **Flexible:** *It's not just a MIDI player!*
|
|
53
|
+
- **Easy to Use:** *Basic setup is just [two lines of code!](https://github.com/spessasus/SpessaSynth/wiki/Usage-As-Library#minimal-setup)*
|
|
54
|
+
- **No dependencies:** *Batteries included!*
|
|
40
55
|
|
|
41
|
-
### Powerful
|
|
56
|
+
### Powerful Synthesizer
|
|
42
57
|
- Suitable for both **real-time** and **offline** synthesis
|
|
43
58
|
- **Excellent SoundFont support:**
|
|
44
59
|
- **Full Generator Support**
|
|
@@ -63,19 +78,22 @@ document.getElementById("button").onclick = async () =>
|
|
|
63
78
|
- **[Custom modulators for additional controllers](https://github.com/spessasus/SpessaSynth/wiki/Modulator-Class#default-modulators):** *Why not?*
|
|
64
79
|
- **Written using AudioWorklets:**
|
|
65
80
|
- Runs in a **separate thread** for maximum performance
|
|
81
|
+
- Doesn't stop playing even when the main thread is frozen
|
|
66
82
|
- Supported by all modern browsers
|
|
67
83
|
- **Unlimited channel count:** Your CPU is the limit!
|
|
68
84
|
- **Excellent MIDI Standards Support:**
|
|
69
85
|
- **MIDI Controller Support:** Default supported controllers [here](https://github.com/spessasus/SpessaSynth/wiki/MIDI-Implementation#supported-controllers)
|
|
86
|
+
- **Portamento Support:** Glide the notes!
|
|
87
|
+
- **Sound Controllers:** Real-time filter and envelope control!
|
|
70
88
|
- **MIDI Tuning Standard Support:** [more info here](https://github.com/spessasus/SpessaSynth/wiki/MIDI-Implementation#midi-tuning-standard)
|
|
71
89
|
- [Full **RPN** and limited **NRPN** support](https://github.com/spessasus/SpessaSynth/wiki/MIDI-Implementation#supported-registered-parameters)
|
|
72
90
|
- Supports some [**Roland GS** and **Yamaha XG** system exclusives](https://github.com/spessasus/SpessaSynth/wiki/MIDI-Implementation#supported-system-exclusives)
|
|
73
|
-
- **High-performance mode:** Play Rush E!
|
|
91
|
+
- **High-performance mode:** Play Rush E! *note: may kill your browser ;)*
|
|
74
92
|
|
|
75
|
-
###
|
|
76
|
-
- **Supports MIDI formats 0, 1, and 2:**
|
|
93
|
+
### Powerful and Fast MIDI Sequencer
|
|
94
|
+
- **Supports MIDI formats 0, 1, and 2:** *note: format 2 support is experimental as it's very, very rare.*
|
|
77
95
|
- **[Multi-Port MIDI](https://github.com/spessasus/SpessaSynth/wiki/About-Multi-Port) support:** More than 16 channels!
|
|
78
|
-
- **Smart preloading:** Only preloads the samples used in the MIDI file for smooth playback (down to key and velocity!)
|
|
96
|
+
- **Smart preloading:** Only preloads the samples used in the MIDI file for smooth playback *(down to key and velocity!)*
|
|
79
97
|
- **Lyrics support:** Add karaoke to your program!
|
|
80
98
|
- **Raw lyrics available:** Decode in any encoding! *(Kanji? No problem!)*
|
|
81
99
|
- **Runs in Audio Thread as well:** Never blocks the main thread
|
|
@@ -89,7 +107,7 @@ document.getElementById("button").onclick = async () =>
|
|
|
89
107
|
- **Used channels on track:** Quickly determine which channels are used
|
|
90
108
|
- **Key range detection:** Detect the key range of the MIDI
|
|
91
109
|
- **Easy MIDI editing:** Use [helper functions](https://github.com/spessasus/SpessaSynth/wiki/Writing-MIDI-Files#modifymidi) to modify the song to your needs!
|
|
92
|
-
- **Loop detection:** Automatically detects loops in MIDIs (e.g., from
|
|
110
|
+
- **Loop detection:** Automatically detects loops in MIDIs (e.g., from *Touhou Project*)
|
|
93
111
|
- **First note detection:** Skip unnecessary silence at the start by jumping to the first note!
|
|
94
112
|
- **Lyrics support:** Both regular MIDI and .kar files!
|
|
95
113
|
- **[Write MIDI files from scratch](https://github.com/spessasus/SpessaSynth/wiki/Creating-MIDI-Files)**
|
package/index.js
CHANGED
|
@@ -8,7 +8,10 @@ import { Generator } from "./soundfont/basic_soundfont/generator.js";
|
|
|
8
8
|
import { Modulator } from "./soundfont/basic_soundfont/modulator.js";
|
|
9
9
|
import { BasicPresetZone } from "./soundfont/basic_soundfont/basic_zones.js";
|
|
10
10
|
import { BasicPreset } from "./soundfont/basic_soundfont/basic_preset.js";
|
|
11
|
+
import { BasicMIDI } from "./midi_parser/basic_midi.js";
|
|
12
|
+
import { MidiMessage } from "./midi_parser/midi_message.js";
|
|
11
13
|
import { MIDI } from './midi_parser/midi_loader.js';
|
|
14
|
+
import { RMIDINFOChunks } from "./midi_parser/rmidi_writer.js";
|
|
12
15
|
import { MIDIticksToSeconds } from './midi_parser/basic_midi.js';
|
|
13
16
|
import { MIDIBuilder } from "./midi_parser/midi_builder.js";
|
|
14
17
|
import { Synthetizer, VOICE_CAP, DEFAULT_PERCUSSION } from './synthetizer/synthetizer.js';
|
|
@@ -32,19 +35,28 @@ import { MIDIDeviceHandler} from "./external_midi/midi_handler.js";
|
|
|
32
35
|
import { WebMidiLinkHandler} from "./external_midi/web_midi_link.js";
|
|
33
36
|
import { formatTime, formatTitle, consoleColors, arrayToHexString } from './utils/other.js';
|
|
34
37
|
import { readBytesAsUintBigEndian } from './utils/byte_functions/big_endian.js';
|
|
38
|
+
import { readBytesAsString } from "./utils/byte_functions/string.js";
|
|
39
|
+
import { readLittleEndian } from "./utils/byte_functions/little_endian.js";
|
|
40
|
+
import { readVariableLengthQuantity } from "./utils/byte_functions/variable_length_quantity.js";
|
|
35
41
|
import { modulatorSources } from "./soundfont/basic_soundfont/modulator.js";
|
|
36
42
|
import { NON_CC_INDEX_OFFSET } from "./synthetizer/worklet_system/worklet_utilities/controller_tables.js";
|
|
37
43
|
import { ALL_CHANNELS_OR_DIFFERENT_ACTION } from './synthetizer/worklet_system/message_protocol/worklet_message.js';
|
|
44
|
+
import { DEFAULT_SYNTH_CONFIG } from "./synthetizer/audio_effects/effects_config.js";
|
|
38
45
|
import { trimSoundfont} from "./soundfont/basic_soundfont/write_sf2/soundfont_trimmer.js";
|
|
39
46
|
import { WORKLET_URL_ABSOLUTE } from './synthetizer/worklet_url.js';
|
|
47
|
+
import { SynthesizerSnapshot} from "./synthetizer/worklet_system/snapshot/synthesizer_snapshot.js";
|
|
48
|
+
import { ChannelSnapshot } from "./synthetizer/worklet_system/snapshot/channel_snapshot.js";
|
|
40
49
|
|
|
41
50
|
// Export modules
|
|
42
51
|
export {
|
|
43
52
|
// Synthesizer and Sequencer
|
|
44
53
|
Sequencer,
|
|
45
54
|
Synthetizer,
|
|
55
|
+
SynthesizerSnapshot,
|
|
56
|
+
ChannelSnapshot,
|
|
46
57
|
DEFAULT_PERCUSSION,
|
|
47
58
|
VOICE_CAP,
|
|
59
|
+
DEFAULT_SYNTH_CONFIG,
|
|
48
60
|
|
|
49
61
|
// SoundFont
|
|
50
62
|
BasicSoundFont,
|
|
@@ -61,13 +73,16 @@ export {
|
|
|
61
73
|
|
|
62
74
|
// MIDI
|
|
63
75
|
MIDI,
|
|
76
|
+
BasicMIDI,
|
|
64
77
|
MIDIBuilder,
|
|
65
78
|
IndexedByteArray,
|
|
79
|
+
MidiMessage,
|
|
66
80
|
writeMIDIFile,
|
|
67
81
|
writeRMIDI,
|
|
68
82
|
applySnapshotToMIDI,
|
|
69
83
|
modifyMIDI,
|
|
70
84
|
MIDIticksToSeconds,
|
|
85
|
+
RMIDINFOChunks,
|
|
71
86
|
|
|
72
87
|
// Utilities
|
|
73
88
|
audioBufferToWav,
|
|
@@ -86,7 +101,12 @@ export {
|
|
|
86
101
|
consoleColors,
|
|
87
102
|
formatTitle,
|
|
88
103
|
formatTime,
|
|
104
|
+
|
|
89
105
|
readBytesAsUintBigEndian,
|
|
106
|
+
readBytesAsString,
|
|
107
|
+
readLittleEndian,
|
|
108
|
+
readVariableLengthQuantity,
|
|
109
|
+
|
|
90
110
|
NON_CC_INDEX_OFFSET,
|
|
91
111
|
ALL_CHANNELS_OR_DIFFERENT_ACTION,
|
|
92
112
|
WORKLET_URL_ABSOLUTE
|
|
@@ -92,9 +92,6 @@ export class BasicMIDI extends MIDISequenceData
|
|
|
92
92
|
* @type {boolean}
|
|
93
93
|
*/
|
|
94
94
|
let karaokeHasTitle = false;
|
|
95
|
-
let portOffset = 0;
|
|
96
|
-
this.midiPorts = [];
|
|
97
|
-
this.midiPortChannelOffsets = [];
|
|
98
95
|
|
|
99
96
|
this.keyRange = { max: 0, min: 127 };
|
|
100
97
|
|
|
@@ -126,7 +123,6 @@ export class BasicMIDI extends MIDISequenceData
|
|
|
126
123
|
{
|
|
127
124
|
const track = this.tracks[i];
|
|
128
125
|
const usedChannels = new Set();
|
|
129
|
-
this.midiPorts.push(-1);
|
|
130
126
|
let trackHasVoiceMessages = false;
|
|
131
127
|
|
|
132
128
|
for (const e of track)
|
|
@@ -230,16 +226,6 @@ export class BasicMIDI extends MIDISequenceData
|
|
|
230
226
|
e.messageData.currentIndex = 0;
|
|
231
227
|
break;
|
|
232
228
|
|
|
233
|
-
case messageTypes.midiPort:
|
|
234
|
-
const port = e.messageData[0];
|
|
235
|
-
this.midiPorts[i] = port;
|
|
236
|
-
if (this.midiPortChannelOffsets[port] === undefined)
|
|
237
|
-
{
|
|
238
|
-
this.midiPortChannelOffsets[port] = portOffset;
|
|
239
|
-
portOffset += 16;
|
|
240
|
-
}
|
|
241
|
-
break;
|
|
242
|
-
|
|
243
229
|
case messageTypes.copyright:
|
|
244
230
|
if (!copyrightDetected)
|
|
245
231
|
{
|
|
@@ -399,6 +385,33 @@ export class BasicMIDI extends MIDISequenceData
|
|
|
399
385
|
consoleColors.recognized
|
|
400
386
|
);
|
|
401
387
|
|
|
388
|
+
// determine ports
|
|
389
|
+
let portOffset = 0;
|
|
390
|
+
this.midiPorts = [];
|
|
391
|
+
this.midiPortChannelOffsets = [];
|
|
392
|
+
for (let trackNum = 0; trackNum < this.tracks.length; trackNum++)
|
|
393
|
+
{
|
|
394
|
+
this.midiPorts.push(-1);
|
|
395
|
+
if (this.usedChannelsOnTrack[trackNum].size === 0)
|
|
396
|
+
{
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
for (const e of this.tracks[trackNum])
|
|
400
|
+
{
|
|
401
|
+
if (e.messageStatusByte !== messageTypes.midiPort)
|
|
402
|
+
{
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
const port = e.messageData[0];
|
|
406
|
+
this.midiPorts[trackNum] = port;
|
|
407
|
+
if (this.midiPortChannelOffsets[port] === undefined)
|
|
408
|
+
{
|
|
409
|
+
this.midiPortChannelOffsets[port] = portOffset;
|
|
410
|
+
portOffset += 16;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
402
415
|
// fix midi ports:
|
|
403
416
|
// midi tracks without ports will have a value of -1
|
|
404
417
|
// if all ports have a value of -1, set it to 0,
|
|
@@ -408,15 +421,21 @@ export class BasicMIDI extends MIDISequenceData
|
|
|
408
421
|
// but leave the conductor track with no port pref.
|
|
409
422
|
// this spessasynth to reserve the first 16 channels for the conductor track
|
|
410
423
|
// (which doesn't play anything) and use the additional 16 for the actual ports.
|
|
411
|
-
let defaultPort =
|
|
424
|
+
let defaultPort = Infinity;
|
|
412
425
|
for (let port of this.midiPorts)
|
|
413
426
|
{
|
|
414
427
|
if (port !== -1)
|
|
415
428
|
{
|
|
416
|
-
defaultPort
|
|
417
|
-
|
|
429
|
+
if (defaultPort > port)
|
|
430
|
+
{
|
|
431
|
+
defaultPort = port;
|
|
432
|
+
}
|
|
418
433
|
}
|
|
419
434
|
}
|
|
435
|
+
if (defaultPort === Infinity)
|
|
436
|
+
{
|
|
437
|
+
defaultPort = 0;
|
|
438
|
+
}
|
|
420
439
|
this.midiPorts = this.midiPorts.map(port => port === -1 ? defaultPort : port);
|
|
421
440
|
// add fake port if empty
|
|
422
441
|
if (this.midiPortChannelOffsets.length === 0)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spessasynth_lib",
|
|
3
|
-
"version": "3.24.
|
|
3
|
+
"version": "3.24.28",
|
|
4
4
|
"description": "MIDI and SoundFont2/DLS library with no compromises",
|
|
5
5
|
"browser": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"type": "git",
|
|
12
12
|
"url": "git+https://github.com/spessasus/SpessaSynth.git"
|
|
13
13
|
},
|
|
14
|
+
"main": "index.js",
|
|
14
15
|
"keywords": [
|
|
15
16
|
"soundfont",
|
|
16
17
|
"synthesizer",
|
package/sequencer/sequencer.js
CHANGED
|
@@ -99,7 +99,7 @@ export class Sequencer
|
|
|
99
99
|
|
|
100
100
|
/**
|
|
101
101
|
* Fires on meta-event
|
|
102
|
-
* @type {Object<string, function([number, Uint8Array])>}
|
|
102
|
+
* @type {Object<string, function([number, Uint8Array, number])>}
|
|
103
103
|
*/
|
|
104
104
|
onMetaEvent = {};
|
|
105
105
|
|
|
@@ -438,7 +438,7 @@ export class Sequencer
|
|
|
438
438
|
|
|
439
439
|
/**
|
|
440
440
|
* Adds a new event that gets called when a meta-event occurs
|
|
441
|
-
* @param callback {function([number, Uint8Array])} the meta-event type and
|
|
441
|
+
* @param callback {function([number, Uint8Array, number])} the meta-event type, its data and the track number
|
|
442
442
|
* @param id {string} must be unique
|
|
443
443
|
*/
|
|
444
444
|
addOnMetaEvent(callback, id)
|
|
@@ -171,7 +171,10 @@ export function _processEvent(event, trackIndex)
|
|
|
171
171
|
}
|
|
172
172
|
if (statusByteData.status >= 0 && statusByteData.status < 0x80)
|
|
173
173
|
{
|
|
174
|
-
this.post(
|
|
174
|
+
this.post(
|
|
175
|
+
WorkletSequencerReturnMessageType.metaEvent,
|
|
176
|
+
[event.messageStatusByte, event.messageData, trackIndex]
|
|
177
|
+
);
|
|
175
178
|
}
|
|
176
179
|
}
|
|
177
180
|
|
|
@@ -47,6 +47,6 @@ export const WorkletSequencerReturnMessageType = {
|
|
|
47
47
|
pause: 4, // no data
|
|
48
48
|
getMIDI: 5, // midiData<MIDI>
|
|
49
49
|
midiError: 6, // errorMSG<string>
|
|
50
|
-
metaEvent: 7, // [messageType<number>, messageData<Uint8Array>]
|
|
50
|
+
metaEvent: 7, // [messageType<number>, messageData<Uint8Array>, trackNum<number>]
|
|
51
51
|
loopCountChange: 8 // newLoopCount<number>
|
|
52
52
|
};
|