spessasynth_core
Version:
MIDI and SoundFont2/DLS library with no compromises
304 lines (248 loc) • 13.4 kB
Markdown
<!--suppress HtmlDeprecatedAttribute, HtmlRequiredAltAttribute, HtmlExtraClosingTag -->
<p align='center'>
<img src='https://raw.githubusercontent.com/spessasus/SpessaSynth/refs/heads/master/src/website/spessasynth_logo_rounded.png' width='300' alt='SpessaSynth logo'>
</p>
*A powerful SF2/DLS/MIDI TypeScript/JavaScript library. It works with any modern JS environment that supports
WebAssembly.*
It allows you to:
- Play MIDI files using SF2/SF3/DLS files!
- Read and write MIDI files!
- Write SF2/SF3 files!
- Convert DLS to SF2! (and back!)
- [and more!](#current-features)
### v4.0.0 TypeScript Update is here!
[**Read about breaking changes here.**](https://spessasus.github.io/spessasynth_core/extra/3-28-migration-guide)
> **Tip:**
>
> Looking for an easy-to-use WebAudioAPI browser wrapper?
> Try [spessasynth_lib](https://github.com/spessasus/spessasynth_lib)!
### [Project site (consider giving it a star!)](https://github.com/spessasus/spessasynth_core)
### Made with spessasynth_core
- [SpessaSynth Online SF2/DLS MIDI Player](https://spessasus.github.io/SpessaSynth)
- [SpessaFont Online SoundFont/DLS Editor](https://spessasus.github.io/SpessaFont)
### [Documentation](https://spessasus.github.io/spessasynth_core)
**SpessaSynth Project index**
- [spessasynth_core](https://github.com/spessasus/spessasynth_core) (you are here) - SF2/DLS/MIDI library
- [spessasynth_lib](https://github.com/spessasus/spessasynth_lib) - spessasynth_core wrapper optimized for browsers and
WebAudioAPI
- [SpessaSynth](https://github.com/spessasus/SpessaSynth) - online/local MIDI player/editor application
- [SpessaFont](https://github.com/spessasus/SpessaFont) - online SF2/DLS editor
## Current Features
### Easy Integration
- **Modular design:** *Easy integration into other projects (load what you need)*
- **Flexible:** *It's not just a MIDI player!*
- **Easy to Use:** *Basic setup is
just [two lines of code!](https://spessasus.github.io/spessasynth_core/getting-started#minimal-setup)*
- **No dependencies:** *Batteries included!*
- **TypeScript definitions:** *Autocompletion in IDEs!*
### Powerful MIDI Synthesizer
- Suitable for both **real-time** and **offline** synthesis
- **Excellent SoundFont support:**
- **Full Generator Support**
- **Full Modulator Support:** *First (to my knowledge) JavaScript SoundFont synth with that feature!*
- **GeneralUserGS Compatible:**
*[See more here!](https://github.com/mrbumpy409/GeneralUser-GS/blob/main/documentation/README.md)*
- **SoundFont3 Support:** Play compressed SoundFonts!
- **Experimental SF2Pack Support:** Play soundfonts compressed with BASSMIDI! (*Note: only works with vorbis
compression*)
- **Can load very large SoundFonts:** up to 4GB! *Note: Only Firefox handles this well; Chromium has a hard-coded
memory limit*
- **Great DLS Support:**
- **DLS Level 1 Support**
- **DLS Level 2 Support**
- **Mobile DLS Support**
- **Correct articulator support:** *Converts articulators to both modulators and generators!*
- **Tested and working with gm.dls!**
- **Correct volume:** *Properly translated to SoundFont volume!*
- **A-Law encoding support**
- **Both unsigned 8-bit and signed 16-bit sample support (24-bit theoretically supported as well!)**
- **Detects special articulator combinations:** *Such as vibratoLfoToPitch*
- **Soundfont manager:** Stack multiple soundfonts!
- **Unlimited channel count:** Your CPU is the limit!
- **Excellent MIDI Standards Support:**
- **MIDI Controller Support:** Default supported
controllers [here](https://spessasus.github.io/spessasynth_core/extra/midi-implementation#default-supported-controllers)
- **Portamento Support:** *Smooth note gliding!*
- **Sound Controllers:** *Real-time filter and envelope control!*
- **MIDI Tuning Standard Support:**
*[more info here](https://spessasus.github.io/spessasynth_core/extra/midi-implementation#midi-tuning-standard)*
- [Full **RPN** and limited **NRPN**
support](https://spessasus.github.io/spessasynth_core/extra/midi-implementation#supported-registered-parameters)
- **SoundFont2 NRPN Support**
- [**AWE32**
NRPN Compatibility Layer](https://spessasus.github.io/spessasynth_core/extra/midi-implementation#awe32-nrpn-compatibility-layer)
- Supports some [**Roland GS** and **Yamaha XG**
system exclusives](https://spessasus.github.io/spessasynth_core/extra/midi-implementation#supported-system-exclusives)
### Powerful and Fast MIDI Sequencer
- **Supports MIDI formats 0, 1, and 2:** *note: format 2 support is experimental as it's very, very rare.*
- **[Multi-Port MIDI](https://spessasus.github.io/spessasynth_core/extra/about-multi-port) support:** *More than 16
channels!*
- **Smart preloading:** Only preloads the samples used in the MIDI file for smooth playback *(down to key and
velocity!)*
- **Lyrics support:** *Add karaoke to your program!*
- **Raw lyrics available:** Decode in any encoding! *(Kanji? No problem!)*
- **Loop points support:** *Ensures seamless loops!*
### Read and Write SoundFont and MIDI Files with Ease
#### Read and write MIDI files
- **Smart name detection:** *Handles incorrectly formatted and non-standard track names!*
- **Raw name available:** Decode in any encoding! *(Kanji? No problem!)*
- **Port detection during load time:** *Manage ports and channels easily!*
- **Used channels on track:** *Quickly determine which channels are used!*
- **Key range detection:** *Detect the key range of the MIDI!*
- **Easy MIDI editing:**
Use [helper functions](https://spessasus.github.io/spessasynth_core/writing-files/midi#modifymidi) to modify the
song to your needs!
- **Loop detection:** *Automatically detects loops in MIDIs (e.g., from **Touhou Project**)*
- **First note detection:** *Skip unnecessary silence at the start by jumping to the first note!*
- **Lyrics support:** *Both regular MIDI and .kar files!*
- **[Write MIDI files from scratch](https://spessasus.github.io/spessasynth_core/midi/creating-midi-files)**
- **Easy saving:** *Save with
just [one function!](https://spessasus.github.io/spessasynth_core/writing-files/midi#writemidi)*
#### Read and write [RMID files with embedded sound banks](https://github.com/spessasus/sf2-rmidi-specification#readme)
- **[Level 4](https://github.com/spessasus/sf2-rmidi-specification#level-4) compliance:** Reads and writes *everything!*
- **Compression and trimming support:** *Reduce a MIDI file with a 1GB sound bank to **as small as 5MB**!*
- **DLS Version support:** *The original legacy format with bank offset detection!*
- **Automatic bank shifting and validation:** Every sound bank *just works!*
- **Metadata support:** Add title, artist, album name and cover and more! And of course, read them too! *(In any
encoding!)*
- **Compatible with [Falcosoft Midi Player 6!](https://falcosoft.hu/softwares.html#midiplayer)**
- **Easy saving:**
*[As simple as saving a MIDI file!](https://spessasus.github.io/spessasynth_core/writing-files/midi#writermidi)*
#### Read and write SoundFont2 files
- **Easy info access:** *Just
an [object of strings!](https://spessasus.github.io/spessasynth_core/sound-bank#soundbankinfo)*
- **Smart trimming:** Trim the sound bank to only include samples used in the MIDI *(down to key and velocity!)*
- **SF3 conversion:** *Compress SoundFont2 files to SoundFont3 with variable quality!*
- **Easy saving:** *Also just [one function!](https://spessasus.github.io/spessasynth_core/sound-bank#write)*
#### Read and write SoundFont3 files
- Same features as SoundFont2 but with now with **Ogg Vorbis compression!**
- **Variable compression quality:** *You choose between file size and quality!*
- **Compression preserving:** *Avoid decompressing and recompressing uncompressed samples for minimal quality loss!*
- **Custom compression function:** *Want a different format than Vorbis? No problem!*
#### Read and write DLS Level One or Two files
- Read DLS (DownLoadable Sounds) files like SF2 files!
- **Native support:** *Saving it as sf2 is
still [just one function!](https://spessasus.github.io/spessasynth_core/sound-bank#write)*
- *That's right, saving as DLS is
also [just one function!](https://spessasus.github.io/spessasynth_core/sound-bank#writedls)*
- Converts articulators to both **modulators** and **generators**!
- Works with both unsigned 8-bit samples and signed 16-bit samples!
- **A-Law encoding support:** *Sure, why not?*
- **Covers special generator cases:** *such as modLfoToPitch*!
- **Correct volume:** *looking at you, Viena and gm.sf2!*
- Support built right into the synthesizer!
- **Convert SF2 to DLS:** [limited support](https://spessasus.github.io/spessasynth_core/extra/dls-conversion-problem)
### Export MIDI as WAV
- Save the MIDI file as WAV audio!
- **Metadata support:** *Embed metadata such as title, artist, album and more!*
- **Cue points:** *Write MIDI loop points as cue points!*
- **Loop multiple times:** *Render two (or more) loops into the file for seamless transitions!*
- *That's right, saving as WAV is
also [just one function!](https://spessasus.github.io/spessasynth_core/writing-files/wav#audiobuffertowav)*
### Limitations
- Synth's performance may be questionable sometimes
- [SF2 to DLS Conversion limits](https://spessasus.github.io/spessasynth_core/extra/dls-conversion-problem)
#### TODO
- Improve the performance of the engine
- Potentially port the system to Emscripten
### Special Thanks
- [FluidSynth](https://github.com/FluidSynth/fluidsynth) - for the source code that helped implement functionality and
fixes
- [Polyphone](https://www.polyphone-soundfonts.com/) - for the soundfont testing and editing tool
- [Meltysynth](https://github.com/sinshu/meltysynth) - for the initial low-pass filter implementation
- [RecordingBlogs](https://www.recordingblogs.com/) - for detailed explanations on MIDI messages
- [stbvorbis.js](https://github.com/hajimehoshi/stbvorbis.js) - for the Vorbis decoder
- [fflate](https://github.com/101arrowz/fflate) - for the MIT DEFLATE implementation
- [tsup](https://github.com/egoist/tsup) - for the TypeScript bundler
- [foo_midi](https://github.com/stuerp/foo_midi) - for useful resources on XMF file format
- [Falcosoft](https://falcosoft.hu) - for help with the RMIDI format
- [Christian Collins](https://schristiancollins.com) - for various bug reports regarding the synthesizer
- **And You!** - for checking out this project. I hope you like it :)
**If you like this project, consider giving it a star. It really helps out!**
### Short example: MIDI to wav converter
```ts
import * as fs from "fs/promises";
import {
audioToWav,
BasicMIDI,
SoundBankLoader,
SpessaSynthProcessor,
SpessaSynthSequencer
} from "spessasynth_core";
// Process arguments
const args = process.argv.slice(2);
if (args.length !== 3) {
console.info(
"Usage: tsx index.ts <soundbank path> <midi path> <wav output path>"
);
process.exit();
}
const sf = await fs.readFile(args[0]);
const mid = await fs.readFile(args[1]);
const midi = BasicMIDI.fromArrayBuffer(mid.buffer);
const sampleRate = 44100;
const sampleCount = Math.ceil(44100 * (midi.duration + 2));
const synth = new SpessaSynthProcessor(sampleRate, {
enableEventSystem: false,
enableEffects: false
});
synth.soundBankManager.addSoundBank(
SoundBankLoader.fromArrayBuffer(sf.buffer),
"main"
);
await synth.processorInitialized;
const seq = new SpessaSynthSequencer(synth);
seq.loadNewSongList([midi]);
seq.play();
const outLeft = new Float32Array(sampleCount);
const outRight = new Float32Array(sampleCount);
const start = performance.now();
let filledSamples = 0;
// Note: buffer size is recommended to be very small, as this is the interval between modulator updates and LFO updates
const BUFFER_SIZE = 128;
let i = 0;
const durationRounded = Math.floor(seq.midiData!.duration * 100) / 100;
const outputArray = [outLeft, outRight];
while (filledSamples < sampleCount) {
// Process sequencer
seq.processTick();
// Render
const bufferSize = Math.min(BUFFER_SIZE, sampleCount - filledSamples);
synth.renderAudio(outputArray, [], [], filledSamples, bufferSize);
filledSamples += bufferSize;
i++;
// Log progress
if (i % 100 === 0) {
console.info(
"Rendered",
Math.floor(seq.currentTime * 100) / 100,
"/",
durationRounded
);
}
}
const rendered = Math.floor(performance.now() - start);
console.info(
"Rendered in",
rendered,
`ms (${Math.floor(((midi.duration * 1000) / rendered) * 100) / 100}x)`
);
const wave = audioToWav([outLeft, outRight], sampleRate);
await fs.writeFile(args[2], new Uint8Array(wave));
console.info(`File written to ${args[2]}`);
```
### Building
To build the NPM package, do:
```bash
npm install
npm run build
```
The files will be placed in the `dist` folder.
## License
Copyright © 2025 Spessasus
Licensed under the Apache-2.0 License.
#### Legal
This project is in no way endorsed or otherwise affiliated with the MIDI Manufacturers Association,
Creative Technology Ltd. or E-mu Systems, Inc., or any other organization mentioned.
SoundFont® is a registered trademark of Creative Technology Ltd.
All other trademarks are the property of their respective owners.