You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
259 lines
8.5 KiB
259 lines
8.5 KiB
PleromaModPlayer = function (attachment) {
|
|
this.file = attachment.querySelector("a").href;
|
|
this.attachment = attachment.cloneNode(true);
|
|
this.attachment.setAttribute("class", "mod-player");
|
|
attachment.parentNode.replaceChild(this.attachment, attachment);
|
|
this.initComponents();
|
|
this.module = null;
|
|
this.replay = null;
|
|
this.patternDisplay = null;
|
|
this.samplePosition = 0;
|
|
this.channel = 0;
|
|
this.patternDisplay = new PatternDisplay(PleromaModTracker.chars.sheet, PleromaModTracker.chars.x, PleromaModTracker.chars.y);
|
|
this.patternCanvas.width = this.patternDisplay.getMaxWidth(PleromaModTracker.chars.x);
|
|
this.patternCanvas.height = this.patternDisplay.getMaxHeight();
|
|
this.loadFile();
|
|
};
|
|
[
|
|
function initComponents () {
|
|
if (!window.CompatAudioContext) {
|
|
const error = document.createElement("div");
|
|
error.setAttribute("class", "alert error");
|
|
error.innerText = "can't use webaudio. check if webaudio is enabled.";
|
|
this.attachment.appendChild(error);
|
|
return;
|
|
}
|
|
|
|
this.songName = this.attachment.querySelector("a");
|
|
this.songName.setAttribute("class", "mod-title");
|
|
|
|
const controls = document.createElement("div");
|
|
controls.setAttribute("class", "mod-controls");
|
|
|
|
this.playButton = document.createElement("button");
|
|
this.playButton.onclick = () => { this.play() };
|
|
this.playButton.innerText = " ▶ ";
|
|
this.playButton.disabled = !window.CompatAudioContext;
|
|
controls.appendChild(this.playButton);
|
|
|
|
this.stopButton = document.createElement("button");
|
|
this.stopButton.onclick = () => { this.stop() };
|
|
this.stopButton.innerText = " ■ ";
|
|
controls.appendChild(this.stopButton);
|
|
|
|
this.seekBar = document.createElement("input");
|
|
this.seekBar.setAttribute("class", "mod-seekbar");
|
|
this.seekBar.setAttribute("type", "range");
|
|
this.seekBar.onmousedown = () => { this.stop() };
|
|
this.seekBar.onmouseup = () => { this.play() };
|
|
controls.appendChild(this.seekBar);
|
|
|
|
this.attachment.appendChild(controls);
|
|
|
|
this.patternCanvas = document.createElement("canvas");
|
|
this.patternCanvas.setAttribute("class", "mod-patterns");
|
|
this.attachment.appendChild(this.patternCanvas);
|
|
|
|
this.instruments = document.createElement("div");
|
|
this.instruments.setAttribute("class", "mod-instruments");
|
|
this.attachment.appendChild(this.instruments);
|
|
},
|
|
|
|
function play () {
|
|
this.stop();
|
|
PleromaModTracker.currentDisplay = this;
|
|
PleromaModTracker.player.setAudioSource(this.replay);
|
|
this.samplePosition = this.replay.seek(this.seekBar.value * PleromaModTracker.player.getSamplingRate());
|
|
PleromaModTracker.setReverb(50);
|
|
PleromaModTracker.player.play();
|
|
},
|
|
function stop () {
|
|
PleromaModTracker.player.stop();
|
|
PleromaModTracker.currentDisplay = null;
|
|
},
|
|
|
|
function loadFile () {
|
|
window.fetch(this.file).then((response) => {
|
|
if (response.ok) {
|
|
response.arrayBuffer().then((buffer) => {
|
|
this.setModule(new Int8Array(buffer));
|
|
}).catch((error) => {
|
|
console.error("can't read module data");
|
|
console.error(error);
|
|
});
|
|
}
|
|
}).catch((error) => {
|
|
console.warn("can't load module");
|
|
console.warn(error);
|
|
});
|
|
},
|
|
|
|
function setModule (moduleData) {
|
|
this.module = new IBXMModule(moduleData);
|
|
const rate = PleromaModTracker.player.getSamplingRate();
|
|
this.replay = new IBXMReplay(this.module, rate);
|
|
const duration = Math.round(this.replay.calculateSongDuration() / rate);
|
|
this.samplePosition = 0;
|
|
this.seekBar.value = 0;
|
|
this.seekBar.max = duration;
|
|
this.songName.innerText = this.module.songName;
|
|
let instruments = "";
|
|
for (let idx = 1; idx < this.module.instruments.length; idx++) {
|
|
const name = this.module.instruments[idx].name;
|
|
if (name.trim().length > 0) {
|
|
const instrument = (idx < 16 ? " " : "") + idx.toString(16);
|
|
instruments = instruments + instrument + " " + name.replace(/\s/g, " ") + "<br/>";
|
|
}
|
|
}
|
|
this.instruments.innerHTML = instruments;
|
|
this.channel = 0;
|
|
this.clearDisplay();
|
|
this.updateDisplay();
|
|
},
|
|
|
|
function updateDisplay (count) {
|
|
if (count) {
|
|
this.samplePosition += count;
|
|
}
|
|
this.seekBar.value = this.samplePosition / PleromaModTracker.player.getSamplingRate();
|
|
this.patternDisplay.display(this.module, this.replay, this.channel, this.patternCanvas);
|
|
},
|
|
|
|
function clearDisplay () {
|
|
const context = this.patternCanvas.getContext("2d");
|
|
context.fillStyle = "black";
|
|
context.fillRect(0, 0, this.patternCanvas.width, this.patternCanvas.height);
|
|
},
|
|
].forEach((fn) => { PleromaModPlayer.prototype[fn.name] = fn; });
|
|
|
|
PleromaModTracker = function () {
|
|
this.config = {
|
|
stylesheet: "style.css",
|
|
pattern: "\\.(xm|mod|s3m)$"
|
|
};
|
|
this.toUpdate = [];
|
|
this.ready = false;
|
|
};
|
|
[
|
|
function onMutation (mutation, observer) {
|
|
for (const addedNode of mutation.addedNodes) {
|
|
this.handlePost(addedNode);
|
|
}
|
|
},
|
|
|
|
function onReady () {
|
|
const posts = document.querySelectorAll(".status");
|
|
for (const post of posts) {
|
|
this.handlePost(post);
|
|
}
|
|
},
|
|
|
|
function handlePost (postElement) {
|
|
if (
|
|
postElement.querySelectorAll &&
|
|
postElement.querySelectorAll(".attachments").length > 0
|
|
) {
|
|
const attachments = this.getModuleAttachments(postElement);
|
|
for (const attachment of attachments) {
|
|
if (this.ready) {
|
|
new PleromaModPlayer(attachment);
|
|
} else {
|
|
this.toUpdate.push(attachment);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
function getModuleAttachments (postElement) {
|
|
const result = [];
|
|
const nonGalleryElements = postElement.querySelectorAll(".non-gallery");
|
|
const regex = new RegExp(this.config.pattern, "i");
|
|
for (const nonGalleryElement of nonGalleryElements) {
|
|
if (
|
|
regex.test(nonGalleryElement.querySelector("a").href)
|
|
) {
|
|
result.push(nonGalleryElement);
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
|
|
function catchUp () {
|
|
for (const attachment of this.toUpdate) {
|
|
new PleromaModPlayer(attachment);
|
|
}
|
|
this.toUpdate = [];
|
|
},
|
|
|
|
function run () {
|
|
PleromaModLoader.includeModCss("pleroma-mod-tracker/" + this.config.stylesheet);
|
|
PleromaModTracker.chars = {
|
|
x: 8,
|
|
y: 16
|
|
};
|
|
if (!window.CompatAudioContext) {
|
|
this.ready = true;
|
|
this.catchUp();
|
|
return;
|
|
}
|
|
Promise.all([
|
|
PleromaModLoader.includeModScript("pleroma-mod-tracker/audio-player.js"),
|
|
PleromaModLoader.includeModScript("pleroma-mod-tracker/ibxm.js"),
|
|
PleromaModLoader.includeModScript("pleroma-mod-tracker/pattern-display.js"),
|
|
]).then(() => {
|
|
const imageCharset = new Image();
|
|
imageCharset.src = PleromaModLoader.getModDir() + "pleroma-mod-tracker/topaz8.png";
|
|
imageCharset.addEventListener("load", () => {
|
|
initCharset(imageCharset, PleromaModTracker.chars.x, PleromaModTracker.chars.y, (chs) => {
|
|
PleromaModTracker.chars.sheet = chs;
|
|
PleromaModTracker.player = new AudioPlayer(
|
|
PleromaModTracker.onPlayerUpdate,
|
|
PleromaModTracker.onEffect
|
|
);
|
|
this.ready = true;
|
|
this.catchUp();
|
|
});
|
|
});
|
|
});
|
|
},
|
|
].forEach((fn) => { PleromaModTracker.prototype[fn.name] = fn; });
|
|
|
|
window.CompatAudioContext = window.AudioContext || window.webkitAudioContext;
|
|
PleromaModTracker.currentDisplay = null;
|
|
|
|
[
|
|
function onPlayerUpdate (count) {
|
|
if (PleromaModTracker.currentDisplay) {
|
|
PleromaModTracker.currentDisplay.updateDisplay(count);
|
|
}
|
|
},
|
|
|
|
function setReverb (milliSeconds) {
|
|
const length = Math.round(PleromaModTracker.player.getSamplingRate() * milliSeconds / 1000);
|
|
PleromaModTracker.reverb = {
|
|
idx: 0,
|
|
length: length,
|
|
bufferLeft: new Float32Array(length),
|
|
bufferRight: new Float32Array(length)
|
|
};
|
|
},
|
|
|
|
function onEffect (mixBufferLeft, mixBufferRight, count) {
|
|
if (PleromaModTracker.reverb.length) {
|
|
const reverb = PleromaModTracker.reverb;
|
|
for (let mixIdx = 0; mixIdx < count; mixIdx++) {
|
|
mixBufferLeft[mixIdx] = (mixBufferLeft[mixIdx] * 3 + reverb.bufferRight[reverb.idx]) * 0.25;
|
|
mixBufferRight[mixIdx] = (mixBufferRight[mixIdx] * 3 + reverb.bufferLeft[reverb.idx]) * 0.25;
|
|
reverb.bufferLeft[reverb.idx] = mixBufferLeft[mixIdx];
|
|
reverb.bufferRight[reverb.idx] = mixBufferRight[mixIdx];
|
|
reverb.idx++;
|
|
if (reverb.idx >= reverb.length) {
|
|
reverb.idx = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
].forEach((fn) => { PleromaModTracker[fn.name] = fn; });
|
|
|
|
PleromaModLoader.registerMod(PleromaModTracker);
|