Advanced Example
The example before is very basic, it only allows uploading a single MIDI file. We can add more features such as play/pause and time controls to our player without much effort.
Let’s add some control buttons:
advanced_demo.html
<p id='message'>Please wait for the sound bank to load.</p>
<input accept='.mid, .rmi, .xmf, .mxmf' id='midi_input' multiple type='file'>
<br><br>
<input id='progress' max='1000' min='0' type='range' value='0'>
<br>
<button id='previous'>Previous song</button>
<button id='pause'>Pause</button>
<button id='next'>Next song</button>
<!-- note the type="module" -->
<script src='advanced_demo.js' type='module'></script>
Now we need to add functionality to those buttons: - Input can now accept more files - Previous song button - Pause button - Next song button - Song progress slider
advanced_demo.js
// import the modules
import { Sequencer, WorkletSynthesizer } from "../../src/index.ts";
import { EXAMPLE_SOUND_BANK_PATH, EXAMPLE_WORKLET_PATH } from "../examples_common.js";
// load the sound bank
fetch(EXAMPLE_SOUND_BANK_PATH).then(async (response) => {
// load the sound bank into an array buffer
let sfBuffer = await response.arrayBuffer();
document.getElementById("message").innerText =
"Sound bank has been loaded!";
// create the context and add audio worklet
const context = new AudioContext();
await context.audioWorklet.addModule(EXAMPLE_WORKLET_PATH);
const synth = new WorkletSynthesizer(context); // create the synthesizer
synth.connect(context.destination);
await synth.soundBankManager.addSoundBank(sfBuffer, "main");
let seq = new Sequencer(synth);
// add an event listener for the file inout
document
.getElementById("midi_input")
.addEventListener("change", async (event) => {
// check if any files are added
const target = /** @type {HTMLInputElement}*/ event.target;
if (target.files.length < 1) {
return;
}
// resume the context if paused
await context.resume();
// parse all the files
const parsedSongs = [];
for (let file of target.files) {
const buffer = await file.arrayBuffer();
parsedSongs.push({
binary: buffer, // binary: the binary data of the file
fileName: file.name // fileName: the fallback name if the MIDI doesn't have one. Here we set it to the file name
});
}
seq.loadNewSongList(parsedSongs); // load the song list
seq.play(); // play the midi
// make the slider move with the song
let slider = document.getElementById("progress");
setInterval(() => {
// slider ranges from 0 to 1000
slider.value = (seq.currentTime / seq.duration) * 1000;
}, 100);
// on song change, show the name
seq.eventHandler.addEvent(
"songChange",
"example-time-change",
(e) => {
document.getElementById("message").innerText =
"Now playing: " + e.getName();
}
); // make sure to add a unique id!
// add time adjustment
slider.onchange = () => {
// calculate the time
seq.currentTime = (slider.value / 1000) * seq.duration; // switch the time (the sequencer adjusts automatically)
};
// add button controls
document.getElementById("previous").onclick = () => {
seq.songIndex--; // go back by one song
};
// on pause click
document.getElementById("pause").onclick = () => {
if (seq.paused) {
document.getElementById("pause").innerText = "Pause";
seq.play(); // resume
} else {
document.getElementById("pause").innerText = "Resume";
seq.pause(); // pause
}
};
document.getElementById("next").onclick = () => {
seq.songIndex++; // go to the next song
};
});
});