Skip to content
spessasus edited this page Jun 15, 2023 · 25 revisions

Welcome to the SpessaSynth wiki! This is the place where I try my best to explain the program.

Introduction

The program is modular to allow it to for example, be sound only (IE. no visualizations).

Current SpessaSynth program structure

Spessasynth structure

Modules explained

Note that the wiki is still under construction, so some links might not be available right now.

  • Synthetizer - Responsible for generating sound.
  • Sequencer - Resposnible for playing the parsed MIDI sequence.
  • Renderer - Responsible for rendering the falling notes and channels' waveforms.
  • SoundFont2 - Responsible for parsing an SF2 file.
  • MIDI parser - Responsible for parsing a MIDI file.
  • MIDI keyboard - Responsible for creating and managing the virtual keyboard on the screen. Also connects to physical MIDI devices using WEB MIDI API.

Library usage

The program makes a heavy usage of the ES6 Modules!

To use the program as a library, simply copy the src folder to your desired destination.

Simple demo

index.html

<h1>SpessaSynth demo</h1>
<p id="message">Please wait for the soundFont to load.</p>
<input type="file" id="midi_input" accept="audio/mid">
<!-- note the type="module" -->
<script src="main.js" type="module"></script>

main.js

// import the modules
import { MIDI } from "./src/js/midi_parser/midi_loader.js";
import { SoundFont2 } from "./src/js/soundfont/soundfont_parser.js";
import { Sequencer } from "./src/js/midi_player/sequencer/sequencer.js";
import { Synthetizer } from "./src/js/midi_player/synthetizer/synthetizer.js";
import {ShiftableByteArray} from "./src/js/utils/shiftable_array.js";

// load the soundfont
fetch("path/to/your/soundfont.sf2").then(async response => {
    // the soundFont files are large, so we need to use readableStreams

    // get the file size
    let size = response.headers.get("content-length");

    // get the reader
    let reader = await (await response.body).getReader();

    // indicates if the readableStream has ended
    let done = false;

    // initialize a new ShitabeByteArray(Uint8Array) of the size
    let dataArray = new ShiftableByteArray(parseInt(size));

    // index of the byte
    let offset = 0;
    do {
        // read a fragment
        let readData = await reader.read();

        // if the stream is still open
        if (readData.value) {
            // put the read data into the array and move the offset
            dataArray.set(readData.value, offset);
            offset += readData.value.length;
        }
        // repeat until the stream has ended
        done = readData.done;
    }while (!done);

    console.log("The soundfont has been loaded!");
    document.getElementById("message").innerText = "SoundFont has been loaded!";

    // parse the soundFont
    const soundFont = new SoundFont2(dataArray);

    // add an event listener
    document.getElementById("midi_input").addEventListener("change", async event => {
        // check if any files are added
        if(!event.target.files[0])
        {
            return;
        }
        const file = event.target.files[0];

        // convert the file to a byte array
        const fileByteArray = new ShiftableByteArray(await file.arrayBuffer());

        // parse the MIDI file
        const parsedMidi = new MIDI(fileByteArray);

        // create an audioContext
        const context = new AudioContext();

        // create the synthetizer
        const synth = new Synthetizer(context.destination, soundFont);

        // create the sequencer
        const seq = new Sequencer(parsedMidi, synth);

        // play
        seq.play();
    })
});

you can also use File to pass the soundFont to the library, as long as you convert it to ShiftableByteArray.