Skip to content

Commit

Permalink
Update soundfont to SGM
Browse files Browse the repository at this point in the history
why? Because most people will go to the demo and select the built-in soundfont, thinking that this is what SpessaSynth sounds like without digging into better soundfonts.
  • Loading branch information
spessasus committed May 19, 2024
1 parent d78ec68 commit 9c58d82
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 216 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ SoundFont2 based realtime synthetizer and MIDI player written in JavaScript usin
- Modular design allows easy integrations into other projects
- Written in pure JavaScript using WebAudio API (Every modern browser supports it)
- No dependencies (Node.js is only required for the app, the core synth and sequencer library needs no dependencies)
- Comes bundled with a small [GeneralUser GS](https://schristiancollins.com/generaluser.php) soundFont to get you started
- Comes bundled with a compressed [SGM](https://musical-artifacts.com/artifacts/855) SoundFont to get you started

### Limitations
- It might not sound as good as other synthetizers (e.g. FluidSynth or BASSMIDI)
Expand Down
216 changes: 2 additions & 214 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ <h1 id="title">SpessaSynth: Online Demo</h1>
</label>

<label id='bundled_sf'>
Use the bundled SoundFont (7MB)
Use the bundled SoundFont (22MB)
</label>
</div>
</div>
Expand All @@ -60,218 +60,6 @@ <h1 id="title">SpessaSynth: Online Demo</h1>
</div>

<script src='src/spessasynth_lib/utils/stbvorbis.js'></script>
<script type="module">
"use strict"

import {Manager} from "./src/website/manager.js";
import {MIDI} from "./src/spessasynth_lib/midi_parser/midi_loader.js";

import {SoundFont2} from "./src/spessasynth_lib/soundfont/soundfont_parser.js";
import {ShiftableByteArray} from "./src/spessasynth_lib/utils/shiftable_array.js";
import { formatTitle } from './src/spessasynth_lib/utils/other.js'


const SF_NAME = "GeneralUser_GS.sf3";
const TITLE = "SpessaSynth: SoundFont2 Javascript Synthetizer Online Demo";

/**
* @type {HTMLHeadingElement}
*/
let titleMessage = document.getElementById("title");

/**
* @type {HTMLInputElement}
*/
let fileInput = document.getElementById("midi_file_input");
fileInput.onclick = e => {
e.preventDefault();
titleMessage.innerText = "You need to upload a SoundFont first";
}


let sfInput = document.getElementById("sf_file_input");
// remove the old files
fileInput.value = "";
fileInput.focus();

async function fetchFont(url, callback)
{
let response = await fetch(url);
if(!response.ok)
{
titleMessage.innerText = "Error downloading soundfont!";
throw response;
}
let size = response.headers.get("content-length");
let reader = await (await response.body).getReader();
let done = false;
let dataArray = new ShiftableByteArray(parseInt(size));
let offset = 0;
do{
let readData = await reader.read();
if(readData.value) {
dataArray.set(readData.value, offset);
offset += readData.value.length;
}
done = readData.done;
let percent = Math.round((offset / size) * 100);
callback(percent);
}while(!done);
return dataArray;
}

document.getElementById("bundled_sf").onclick = () => {
titleMessage.innerText = "Downloading SoundFont...";
fetchFont(`soundfonts/${SF_NAME}`, percent => titleMessage.innerText = `Loading SF3: ${percent}%`).then(arr => {
try {
window.soundFontParser = new SoundFont2(arr);
document.getElementById("sf_upload").innerText = SF_NAME;
}
catch (e)
{
titleMessage.innerHTML = `Error parsing soundfont: <pre style='font-family: monospace; font-weight: bold'>${e}</pre>`;
console.log(e);
return;
}
prepareUI();
});
}


/**
* @param midiFile {File}
* @returns {Promise<MIDI>}
*/
async function parseMidi(midiFile)
{
const buffer = await midiFile.arrayBuffer();
const arr = new ShiftableByteArray(buffer);
try {
return new MIDI(arr, midiFile.name);
}
catch (e) {
titleMessage.innerHTML = `Error parsing MIDI: <pre style='font-family: monospace; font-weight: bold'>${e}</pre>`;
throw e;
}
}

/**
* @param midiFiles {FileList}
*/
async function startMidi(midiFiles)
{
let fName;
if(midiFiles[0].name.length > 20)
{
fName = midiFiles[0].name.substring(0, 21) + "...";
}
else
{
fName = midiFiles[0].name;
}
if(midiFiles.length > 1)
{
fName += ` and ${midiFiles.length - 1} others`;
}
document.getElementById("file_upload").innerText = fName;
/**
* @type {MIDI[]}
*/
const parsed = [];

/**
* @type {string[]}
*/
const titles = [];
for (let i = 0; i < midiFiles.length; i++) {
titleMessage.innerText = `Parsing ${midiFiles[i].name}`;
parsed.push(await parseMidi(midiFiles[i]));

let title;
if(parsed[i].midiName.trim().length > 0)
{
title = parsed[i].midiName.trim();
}
else
{
title = formatTitle(midiFiles[i].name);
}
titles.push(title);
}
titleMessage.style.fontStyle = "italic";
document.title = titles[0];
titleMessage.innerText = titles[0]

if(manager.seq)
{
manager.seq.loadNewSongList(parsed);

}
else {
manager.play(parsed);
}
manager.seqUI.setSongTitles(titles);
}

/**
* @param e {{target: HTMLInputElement}}
* @return {Promise<void>}
*/
sfInput.onchange = async e => {
if(!e.target.files[0])
{
return;
}
/**
* @type {File}
*/
const file = e.target.files[0];

document.getElementById("sf_upload").innerText = file.name;
titleMessage.innerText = "Parsing SoundFont...";

const arr = await file.arrayBuffer();
try {
window.soundFontParser = new SoundFont2(new ShiftableByteArray(arr));
}
catch (e)
{
titleMessage.innerHTML = `Error parsing SoundFont: <pre style='font-family: monospace; font-weight: bold'>${e}</pre>`;
console.log(e);
return;
}
prepareUI();
}

function prepareUI()
{
titleMessage.innerText = TITLE;
document.getElementById("bundled_sf").style.display = "none";
document.getElementById("bundled_sf").onclick = undefined;

window.audioContextMain = new AudioContext({sampleRate: 44100});

// prepare midi interface
window.manager = new Manager(audioContextMain, soundFontParser);

sfInput.onchange = undefined;
if(fileInput.files[0])
{
startMidi(fileInput.files);
}
else
{
fileInput.onclick = undefined;
fileInput.onchange = e => {
if(e.target.files[0])
{
startMidi(fileInput.files);
}
}
}
}


</script>
<script type="module" src='src/website/demo_main.js'></script>
</body>
</html>
2 changes: 1 addition & 1 deletion soundfonts/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
*
!.gitignore
!GeneralUser_GS.sf3
!SGM.sf3
Binary file removed soundfonts/GeneralUser_GS.sf3
Binary file not shown.
Binary file added soundfonts/SGM.sf3
Binary file not shown.
Loading

0 comments on commit 9c58d82

Please sign in to comment.