Skip to content

Commit

Permalink
The MIDI Builder Update
Browse files Browse the repository at this point in the history
Added the MIDI builder: create MIDI files from scratch!
Initial DLS tests, currently unused!
  • Loading branch information
spessasus committed Aug 22, 2024
1 parent 3d0d7e4 commit 539433d
Show file tree
Hide file tree
Showing 43 changed files with 812 additions and 222 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,22 +74,23 @@ This repository contains both the library and a complete musical web application
- **Easy MIDI editing:** Use [helper functions](https://github.com/spessasus/SpessaSynth/wiki/Writing-MIDI-Files#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!
- **[Write MIDI files from scratch](https://github.com/spessasus/SpessaSynth/wiki/Creating-MIDI-Files.md)**
- **Easy saving:** Save with just [one function!](https://github.com/spessasus/SpessaSynth/wiki/Writing-MIDI-Files#writemidifile)
-

#### Read and write [RMID files with embedded SF2 soundfonts](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 soundfont to **as small as 5MB**!
- **Automatic bank shifting and validation:** Every soundfont *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://github.com/spessasus/SpessaSynth/wiki/Writing-MIDI-Files#writermidi)
-

#### Read and write SoundFont2 files
- **Easy info access:** Just an [object of strings!](https://github.com/spessasus/SpessaSynth/wiki/SoundFont2-Class#soundfontinfo)
- **Smart trimming:** Trim the SoundFont 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://github.com/spessasus/SpessaSynth/wiki/SoundFont2-Class#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!
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "SpessaSynth",
"version": "3.16.5",
"version": "3.17.0",
"type": "module",
"scripts": {
"start": "node src/website/server/server.js"
Expand Down
32 changes: 17 additions & 15 deletions src/spessasynth_lib/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,24 @@ document.getElementById("button").onclick = async () => {
### Powerful SoundFont Synthesizer
- Suitable for both **real-time** and **offline** synthesis
- **Excellent SoundFont support:**
- **Generator Support**
- **Modulator Support:** _First (to my knowledge) JavaScript SoundFont synth with that feature!_
- **SoundFont3 Support:** Play compressed SoundFonts!
- **Can load very large SoundFonts:** up to 4GB! _Note: Only Firefox handles this well; Chromium has a hard-coded memory limit_
- **Soundfont manager:** Stack multiple soundfonts!
- **Generator Support**
- **Modulator Support:** *First (to my knowledge) JavaScript SoundFont synth with that feature!*
- **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*
- **Soundfont manager:** Stack multiple soundfonts!
- **Reverb and chorus support:** [customizable!](https://github.com/spessasus/SpessaSynth/wiki/Synthetizer-Class#effects-configuration-object)
- **Export audio files** using [OfflineAudioContext](https://developer.mozilla.org/en-US/docs/Web/API/OfflineAudioContext)
- **[Custom modulators for additional controllers](https://github.com/spessasus/SpessaSynth/wiki/Modulator-Class#default-modulators):** Why not?
- **Written using AudioWorklets:**
- Runs in a **separate thread** for maximum performance
- Supported by all modern browsers
- Runs in a **separate thread** for maximum performance
- Supported by all modern browsers
- **Unlimited channel count:** Your CPU is the limit!
- **Excellent MIDI Standards Support:**
- **MIDI Controller Support:** Default supported controllers [here](https://github.com/spessasus/SpessaSynth/wiki/MIDI-Implementation#supported-controllers)
- **MIDI Tuning Standard Support:** [more info here](https://github.com/spessasus/SpessaSynth/wiki/MIDI-Implementation#midi-tuning-standard)
- [Full **RPN** and limited **NRPN** support](https://github.com/spessasus/SpessaSynth/wiki/MIDI-Implementation#supported-registered-parameters)
- **MIDI Tuning Standard Support:** [more info here](https://github.com/spessasus/SpessaSynth/wiki/MIDI-Implementation#midi-tuning-standard)
- Supports some [**Roland GS** and **Yamaha XG** system exclusives](https://github.com/spessasus/SpessaSynth/wiki/MIDI-Implementation#supported-system-exclusives)
- **MIDI Controller Support:** Default supported controllers [here](https://github.com/spessasus/SpessaSynth/wiki/MIDI-Implementation#supported-controllers)
- **MIDI Tuning Standard Support:** [more info here](https://github.com/spessasus/SpessaSynth/wiki/MIDI-Implementation#midi-tuning-standard)
- [Full **RPN** and limited **NRPN** support](https://github.com/spessasus/SpessaSynth/wiki/MIDI-Implementation#supported-registered-parameters)
- Supports some [**Roland GS** and **Yamaha XG** system exclusives](https://github.com/spessasus/SpessaSynth/wiki/MIDI-Implementation#supported-system-exclusives)

- **High-performance mode:** Play Rush E! _note: may kill your browser ;)_

Expand All @@ -76,25 +76,27 @@ document.getElementById("button").onclick = async () => {
- **Easy MIDI editing:** Use [helper functions](https://github.com/spessasus/SpessaSynth/wiki/Writing-MIDI-Files#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!
- **[Write MIDI files from scratch](https://github.com/spessasus/SpessaSynth/wiki/Creating-MIDI-Files.md)**
- **Easy saving:** Save with just [one function!](https://github.com/spessasus/SpessaSynth/wiki/Writing-MIDI-Files#writemidifile)
-

#### Read and write [RMID files with embedded SF2 soundfonts](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 soundfont to **as small as 5MB**!
- **Automatic bank shifting and validation:** Every soundfont *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://github.com/spessasus/SpessaSynth/wiki/Writing-MIDI-Files#writermidi)
-

#### Read and write SoundFont2 files
- **Easy info access:** Just an [object of strings!](https://github.com/spessasus/SpessaSynth/wiki/SoundFont2-Class#soundfontinfo)
- **Smart trimming:** Trim the SoundFont 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://github.com/spessasus/SpessaSynth/wiki/SoundFont2-Class#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!

## License
MIT License, except for the stbvorbis_sync.js in the `externals` folder which is licensed under the Apache-2.0 license.
File renamed without changes.
146 changes: 146 additions & 0 deletions src/spessasynth_lib/midi_parser/basic_midi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
export class BasicMIDI
{
constructor()
{
/**
* The time division of the sequence
* @type {number}
*/
this.timeDivision = 0;
/**
* The duration of the sequence, in seconds
* @type {number}
*/
this.duration = 0;
/**
* The tempo changes in the sequence, ordered from last to first
* @type {{ticks: number, tempo: number}[]}
*/
this.tempoChanges = [{ticks: 0, tempo: 120}];
/**
* Contains the copyright strings
* @type {string}
*/
this.copyright = "";

/**
* The amount of tracks in the sequence
* @type {number}
*/
this.tracksAmount = 0;

/**
* The lyrics of the sequence as binary chunks
* @type {Uint8Array[]}
*/
this.lyrics = [];

/**
* First note on of the MIDI file
* @type {number}
*/
this.firstNoteOn = 0;

/**
* The MIDI's key range
* @type {{min: number, max: number}}
*/
this.keyRange = { min: 0, max: 127 };

/**
* The last voice (note on, off, cc change etc.) event tick
* @type {number}
*/
this.lastVoiceEventTick = 0;

/**
* Midi port numbers for each track
* @type {number[]}
*/
this.midiPorts = [0];

/**
* Channel offsets for each port, using the SpessaSynth method
* @type {number[]}
*/
this.midiPortChannelOffsets = [0];

/**
* All channels that each track uses
* @type {Set<number>[]}
*/
this.usedChannelsOnTrack = [];

/**
* The loop points (in ticks) of the sequence
* @type {{start: number, end: number}}
*/
this.loop = { start: 0, end: 0 };

/**
* The sequence's name
* @type {string}
*/
this.midiName = "";

/**
* The file name of the sequence, if provided in the MIDI class
* @type {string}
*/
this.fileName = "";

/**
* The raw, encoded MIDI name.
* @type {Uint8Array}
*/
this.rawMidiName = undefined;

/**
* The MIDI's embedded soundfont
* @type {ArrayBuffer|undefined}
*/
this.embeddedSoundFont = undefined;

/**
* The MIDI file's format
* @type {number}
*/
this.format = 0;

/**
* The RMID Info data if RMID, otherwise undefined
* @type {Object<string, IndexedByteArray>}
*/
this.RMIDInfo = {};
/**
* The bank offset for RMIDI
* @type {number}
*/
this.bankOffset = 0;

/**
* The actual track data of the MIDI file
* @type {MidiMessage[][]}
*/
this.tracks = [];
}

/**
* Coverts ticks to time in seconds
* @param ticks {number}
* @returns {number}
* @protected
*/
_ticksToSeconds(ticks)
{
if (ticks <= 0) {
return 0;
}

// find the last tempo change that has occured
let tempo = this.tempoChanges.find(v => v.ticks < ticks);

let timeSinceLastTempo = ticks - tempo.ticks;
return this._ticksToSeconds(ticks - timeSinceLastTempo) + (timeSinceLastTempo * 60) / (tempo.tempo * this.timeDivision);
}
}
Loading

0 comments on commit 539433d

Please sign in to comment.