Own XM/MOD replayer, audio mixer with crossfades, synthesised chip sound effects, music director
- src/xm.rs: FastTracker 2 XM and ProTracker MOD replayer (verified against libopenmpt) - src/audio.rs: music players with crossfade + effect voices, rendered on demand - src/sfx.rs: 29 synthesised chip sound effects - www/dc_plugin.js: AudioContext started on first input, streams samples from WASM - music follows the game: title, rotating build tracks, battle, gate assault, jingles - macroquad audio removed; all tests run natively with cargo test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2dba65be8b
commit
925be17e82
13 files changed
+1711
-205
No files matched your search
+40
-1
@@ -1,4 +1,4 @@
|
||||
// DungeonCraft browser plugin for miniquad: storage, file download/upload, save-on-leave.
|
||||
// DungeonCraft browser plugin for miniquad: storage, file download/upload, save-on-leave, audio output.
|
||||
(function () {
|
||||
"use strict";
|
||||
var pending = null; // value handed over by dc_storage_get
|
||||
@@ -77,5 +77,44 @@
|
||||
};
|
||||
}
|
||||
|
||||
// ---- audio: the Rust mixer renders on demand (dc_audio_render), started by the first user input
|
||||
var actx = null;
|
||||
var stats = { frames: 0, peak: 0 };
|
||||
// diagnostics, e.g. for automated tests: state of the audio output
|
||||
window.dc_audio_stats = function () {
|
||||
return { state: actx ? actx.state : "not started", frames: stats.frames, peak: stats.peak };
|
||||
};
|
||||
function startAudio() {
|
||||
if (typeof wasm_exports === "undefined" || !wasm_exports.dc_audio_render) return;
|
||||
if (actx) {
|
||||
if (actx.state !== "running") actx.resume();
|
||||
return;
|
||||
}
|
||||
var AC = window.AudioContext || window.webkitAudioContext;
|
||||
if (!AC) return;
|
||||
actx = new AC();
|
||||
wasm_exports.dc_audio_init(actx.sampleRate);
|
||||
var node = actx.createScriptProcessor(2048, 0, 2);
|
||||
node.onaudioprocess = function (e) {
|
||||
var n = e.outputBuffer.length;
|
||||
var l = e.outputBuffer.getChannelData(0);
|
||||
var r = e.outputBuffer.getChannelData(1);
|
||||
var ptr = wasm_exports.dc_audio_render(n);
|
||||
if (!ptr) { l.fill(0); r.fill(0); return; }
|
||||
var buf = new Float32Array(wasm_memory.buffer, ptr, n * 2);
|
||||
var peak = 0;
|
||||
for (var i = 0; i < n; i++) {
|
||||
l[i] = buf[2 * i]; r[i] = buf[2 * i + 1];
|
||||
var a = Math.abs(l[i]); if (a > peak) peak = a;
|
||||
}
|
||||
stats.frames += n;
|
||||
stats.peak = Math.max(stats.peak * 0.9, peak);
|
||||
};
|
||||
node.connect(actx.destination);
|
||||
}
|
||||
["mousedown", "keydown", "touchstart"].forEach(function (ev) {
|
||||
window.addEventListener(ev, startAudio, true);
|
||||
});
|
||||
|
||||
miniquad_add_plugin({ register_plugin: register, version: 1, name: "dungeoncraft" });
|
||||
})();
|
||||
Reference in new issue
Block a user