<html>
<head>
<meta charset="utf-8">
<link rel="icon" type="image/png" href="icon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="icon-64x64.png" sizes="64x64" />
<link rel="icon" type="image/png" href="icon-128x128.png" sizes="128x128" />
<link rel="icon" type="image/png" href="icon-256x256.png" sizes="256x256" />
<link rel="manifest" href="manifest.json">
<link href="./primer.css" rel="stylesheet" />
<title>FFMML Player</title>
</head>
<body>
<div class="m-3" align="center">
<h1>FFMML Player</h1>
<a href="https://www.nesdev.org/mck_guide_v1.0.txt">MML (MCK) Guide</a> |
<a href="https://github.com/sile/ffmml">FFMML GitHub Repository</a>
<br /><br />
<div>
<textarea id="mml" rows="20" cols="60" class="text-mono"></textarea>
</div>
<br />
<div>
<input value="Play Music" type="button" onclick="playAudio()" class="btn">
<input value="Update URL" type="button" onclick="updateUrl()" class="btn">
<input value="Download .wav" type="button" onclick="downloadWav()" class="btn">
<input value="Share" type="button" onclick="share()" class="btn">
</div>
<br />
<div>
<textarea id="errorMessageArea"
class="text-mono"
style="color:red; font-weight:bold; border:none; resize:none; display:none"
readonly rows="6" cols="80">
</textarea>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/pagurus@0.6.1/dist/pagurus.min.js"></script>
<script>
const params = new URLSearchParams(window.location.search);
if (params.get('mml')) {
document.getElementById('mml').value = params.get('mml');
}
var system = undefined;
var game = undefined;
function parseScript() {
const mml = document.getElementById("mml").value;
game.command(system, "parseScript", new TextEncoder().encode(mml));
}
function playAudio() {
try {
parseScript();
game.command(system, "playAudio", new Uint8Array(0));
clearErrorMessage();
} catch (e) {
console.warn(e);
setErrorMessage(JSON.parse(e.message).message);
}
}
function updateUrl() {
try {
parseScript();
const mml = document.getElementById("mml").value;
const params = new URLSearchParams();
params.set('mml', mml);
window.location.search = params.toString();
clearErrorMessage();
} catch (e) {
setErrorMessage(JSON.parse(e.message).message);
}
}
function share() {
try {
parseScript();
const mml = document.getElementById("mml").value;
const params = new URLSearchParams();
params.set('mml', mml);
const url = new URL(window.location.href);
url.params = params.toString();
console.log("```\n" + mml.trim() + "\n```");
navigator.share({
title: getTitle(),
text: "```\n" + mml.trim() + "\n```\n",
url,
});
} catch (e) {
setErrorMessage(JSON.parse(e.message).message);
}
}
function getTitle() {
const titleBytes = game.query(system, "title");
if (titleBytes.length == 0) {
return `mml-${now()}`;
} else {
return new TextDecoder('utf8').decode(titleBytes);
}
}
function downloadWav() {
let blob;
try {
parseScript();
const data = game.query(system, "exportWav");
blob = new Blob([data], {type: 'audio/x-wav'});
clearErrorMessage();
} catch (e) {
console.warn(e);
setErrorMessage(JSON.parse(e.message).message);
return;
}
const element = document.createElement("a");
element.download = `${getTitle()}.wav`;
element.href = URL.createObjectURL(blob);
element.click();
}
function clearErrorMessage() {
const errorMessage = document.getElementById("errorMessageArea");
errorMessage.style.display = "none";
errorMessage.value = "";
}
function setErrorMessage(msg) {
const errorMessage = document.getElementById("errorMessageArea");
errorMessage.style.display = "block";
errorMessage.value = "ERROR: " + msg;
}
function now() {
return new Intl.DateTimeFormat(
[],
{
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
}
).format(new Date()).replaceAll(/[:/]/g, '').replace(' ', '_');
}
Pagurus.Game.load("ffmml.wasm").then(async gameObject => {
game = gameObject;
system = await Pagurus.System.create(game.memory);
game.initialize(system);
while (true) {
const event = await system.nextEvent();
try {
if (!game.handleEvent(system, event)) {
break;
}
} catch (e) {
console.warn(e);
setErrorMessage(JSON.parse(e.message).message);
}
}
});
</script>
</body>
</html>